diff --git a/src/i18n/common.json b/src/i18n/common.json index 1c4651670..f31800d76 100644 --- a/src/i18n/common.json +++ b/src/i18n/common.json @@ -323,6 +323,7 @@ "validate_serving": "Validate (serving)", "skip": "Skip", "invalid_image": "Invalid image", + "missingValues": "It seems that some inputs are empty for nutriments OFF already knows. Either fill those inputs with appropriate value, or put a \"-\" to indicate that the value is not available.", "pictures": { "picture_date": "Photo uploaded", "selected_as_nutrients": "Selected as nutriment image ✅", diff --git a/src/i18n/en.json b/src/i18n/en.json index 1c4651670..f31800d76 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -323,6 +323,7 @@ "validate_serving": "Validate (serving)", "skip": "Skip", "invalid_image": "Invalid image", + "missingValues": "It seems that some inputs are empty for nutriments OFF already knows. Either fill those inputs with appropriate value, or put a \"-\" to indicate that the value is not available.", "pictures": { "picture_date": "Photo uploaded", "selected_as_nutrients": "Selected as nutriment image ✅", diff --git a/src/pages/nutrition/index.tsx b/src/pages/nutrition/index.tsx index 72f40370c..bd9f27121 100644 --- a/src/pages/nutrition/index.tsx +++ b/src/pages/nutrition/index.tsx @@ -8,6 +8,7 @@ import { Checkbox, FormControlLabel, Typography, + Alert, } from "@mui/material"; import Stack from "@mui/material/Stack"; import { @@ -26,6 +27,7 @@ import Instructions from "./Instructions"; import useNutrimentTranslations from "./useNutrimentTranslations"; import { useCountry } from "../../contexts/CountryProvider"; import { useTranslation } from "react-i18next"; +import { doesNotRemoveData } from "./preventDataLost"; export default function Nutrition() { const { t } = useTranslation(); @@ -76,6 +78,32 @@ export default function Nutrition() { return NUTRIMENTS.filter((item) => !displayedIds.has(item.id)); }, [nutrimentsDisplayed]); + const allOFF100gHaveValues = React.useMemo(() => { + // Every nutriment that has a value in the OFF DB should have a value in database should have a value in the form. + // We ask users to set "-" if the nutrient is not provided on the packaging. + return nutrimentsDisplayed.every(({ id: nutrimentId }) => + doesNotRemoveData({ + nutrimentId, + values, + nutriments: product?.nutriments, + category: "100g", + }), + ); + }, [nutrimentsDisplayed, values, product]); + + const allOFFServingHaveValues = React.useMemo(() => { + // Every nutriment that has a value in the OFF DB should have a value in database should have a value in the form. + // We ask users to set "-" if the nutrient is not provided on the packaging. + return nutrimentsDisplayed.every(({ id: nutrimentId }) => + doesNotRemoveData({ + nutrimentId, + values, + nutriments: product?.nutriments, + category: "serving", + }), + ); + }, [nutrimentsDisplayed, values, product]); + return ( @@ -279,10 +307,8 @@ export default function Nutrition() { tabIndex={1} variant="contained" color="success" - sx={{ - ml: 1, - mt: 2, - }} + sx={{ ml: 1, mt: 2 }} + disabled={!allOFF100gHaveValues} onClick={() => { postRobotoff({ insightId: insight.id, @@ -302,6 +328,7 @@ export default function Nutrition() { variant="contained" color="success" sx={{ ml: 1, mt: 2 }} + disabled={!allOFFServingHaveValues} onClick={() => { postRobotoff({ insightId: insight.id, @@ -318,6 +345,9 @@ export default function Nutrition() { + {(!allOFF100gHaveValues || !allOFFServingHaveValues) && ( + {t("nutrition.missingValues")} + )} diff --git a/src/pages/nutrition/preventDataLost.ts b/src/pages/nutrition/preventDataLost.ts new file mode 100644 index 000000000..7ae1d25c9 --- /dev/null +++ b/src/pages/nutrition/preventDataLost.ts @@ -0,0 +1,41 @@ +import { NutrimentPrediction } from "./insight.types"; + +function hasValue(v: Pick) { + return v && v.value !== null && v.value !== undefined && v.value !== ""; +} +export function doesNotRemoveData({ + nutrimentId, + values, + nutriments, + category, +}: { + nutrimentId: string; + values: Record>; + nutriments?: any; + category: "serving" | "100g"; +}) { + if (nutriments === undefined) { + return true; + } + if (hasValue(values[`${nutrimentId}_${category}`])) { + // The form has value for this nutrient + return true; + } + if ( + nutriments?.[`${nutrimentId}_serving`] == null && + nutriments?.[`${nutrimentId}_100g`] == null + ) { + // OFF does not have value for this nutrient. + // We check 100g and serving, to avoid validating empty serving if 100g are defined. + return true; + } + + // Exceptions for sodium if we have value for the sodium or the inverse + if (nutrimentId === "sodium" && hasValue(values[`salt_${category}`])) { + return true; // no sodium, but salt + } + if (nutrimentId === "salt" && hasValue(values[`sodium_${category}`])) { + return true; // no salt but sodium + } + return false; +}