Source: pages-sections/Foodhealth-sections/ManualContent.js

import React, { useEffect, useState, useMemo } from "react";
import Card from "/components/Card/Card.js";
import CardBody from "/components/Card/CardBody.js";
import CardFooter from "/components/Card/CardFooter.js";
import CardHeader from "/components/Card/CardHeader.js";
import GridContainer from "/components/Grid/GridContainer.js";
import GridItem from "/components/Grid/GridItem.js";
import CustomInput from "/components/CustomInput/CustomInput.js";
import Link from "next/link";
import CardMedia from "@material-ui/core/CardMedia";
import CardActionArea from "@material-ui/core/CardActionArea";
import InputAdornment from "@material-ui/core/InputAdornment";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Switch from "@material-ui/core/Switch";
// nodejs library that concatenates classes
import classNames from "classnames";
// @material-ui/core components
import { makeStyles } from "@material-ui/core/styles";
import { debounce } from "lodash"; // Import debounce function
import styles from "/styles/jss/nextjs-material-kit/pages/components.js";
import {
  getDatabase,
  ref,
  get,
  query,
  orderByChild,
  equalTo,
} from "firebase/database";

/** The line `export const database = getDatabase();` is exporting a constant named `database` that is
assigned the result of calling the `getDatabase()` function. In this context, `getDatabase()` is
likely a function provided by Firebase SDK that initializes and returns a reference to the Firebase
Realtime Database. */
export const database = getDatabase();
const useStyles = makeStyles(styles);

/**  These lines of code are setting up state variables using the `useState` hook in React. Here's what
  each state variable is responsible for: */
/**
 * State to check state of radiobutton for each food item
 */
export const [checkedStates, setCheckedStates] = useState([]);
/**
 * State to store queried food
 */
export const [searchedFood, setSearchedFood] = useState("");
/**
 * State to store food data
 */
export const [foods, setFood] = useState([]);
/**
 * State to store debounced search term
 */
export const [debouncedSearch, setDebouncedSearch] = useState("");

/**
 * The `ManualContentPill` function in JavaScript fetches and filters food data based on a search
 * query, debounces the search input, and displays food information with toggleable serving options.
 * @returns The `ManualContentPill` component is returning a grid layout containing a search input
 * field for food search, cards displaying food information such as calories, fats, carbs, and protein,
 * along with a toggle switch to show the nutritional values per serving or per 100g. Each card also
 * has a "More Info" button that links to a FoodInfo page for detailed information about the specific
 * food item
 */
export function ManualContentPill() {
  const [checkedStates, setCheckedStates] = useState([]);
  const [searchedFood, setSearchedFood] = useState("");
  const [foods, setFood] = useState([]);
  const [debouncedSearch, setDebouncedSearch] = useState("");
  useEffect(() => {
    const fetchFoods = async () => {
      if (debouncedSearch.trim() === "") {
        setFood([]);
        return;
      }

      const foodRef = ref(database, "food/");
      try {
        const snapshot = await get(foodRef);
        if (snapshot.exists()) {
          const foodArray = Object.entries(snapshot.val()).map(
            ([idd, data]) => ({
              idd,
              ...data,
            })
          );
          const filteredFoods = foodArray.filter((food) =>
            food.idd.toLowerCase().includes(debouncedSearch.toLowerCase())
          );
          setFood(filteredFoods);
          setCheckedStates(Array(foodArray.length).fill(false));
        } else {
          console.log("No data available");
        }
      } catch (error) {
        console.error("Error retrieving data:", error);
      }
    };

    fetchFoods();
  }, [debouncedSearch]);
  return (
    <GridContainer justify="left" spacing={4}>
      <GridItem xs={12}>
        <CustomInput
          labelText="Search Food"
          id="float"
          formControlProps={{
            fullWidth: true,
          }}
          inputProps={{
            type: "text",
            onChange: handleFoodSearch,
            endAdornment: <InputAdornment position="end"></InputAdornment>,
          }}
        />
      </GridItem>
      {cachedFoods.map((food, index) => (
        <GridItem xs={12} sm={6} md={4} lg={4} key={food.idd}>
          <Card style={{ backgroundColor: "#fff9c4" }}>
            <CardMedia
              align="left"
              component="img"
              alt="Image cannot be loaded"
              width="100"
              height="200"
              image={
                food.category !== "Others"
                  ? `/img/foods/${food.idd}.jpg`
                  : "/img/fudpic.jpg"
              }
              title="Picture of a food"
            />
            <CardBody>
              <h4 className={food.idd} style={{ fontWeight: "bold" }}>
                {food.idd}
              </h4>
              <p>
                Calories:{" "}
                {checkedStates[index] ? food.scalories : food.calories}
              </p>
              <p>Fats: {checkedStates[index] ? food.sfats : food.fats}</p>
              <p>Carbs: {checkedStates[index] ? food.scarbs : food.carbs}</p>
              <p>
                Protein: {checkedStates[index] ? food.sprotein : food.protein}
              </p>
            </CardBody>
            <CardFooter>
              {/* More Info button linking to FoodInfo page */}
              <Link href={`/foodinfo?id=${food.id}`}>
                <a>More Info</a>
              </Link>
            </CardFooter>
            <div
              style={{
                position: "absolute",
                bottom: 0,
                right: 0,
                margin: "16px",
              }}
            >
              <FormControlLabel
                control={
                  <Switch
                    checked={checkedStates[index]}
                    onChange={() => handleToggleChange(index)}
                    value={`checked${index}`}
                    classes={{
                      switchBase: classes.switchBase,
                      checked: classes.switchChecked,
                      thumb: classes.switchIcon,
                      track: classes.switchBar,
                    }}
                  />
                }
                id={{
                  label: food.idd,
                }}
                label={
                  checkedStates[index]
                    ? "Showing per serving"
                    : "Showing per 100g"
                }
              />
            </div>
          </Card>
        </GridItem>
      ))}
    </GridContainer>
  );
}

/**
 * The function fetches food data from a database, filters it based on a search query, and updates the
 * state accordingly.
 * @returns If the `debouncedSearch` is empty after trimming, the function will set the `food` state to
 * an empty array and then return.
 */
export const fetchFoods = async () => {
  if (debouncedSearch.trim() === "") {
    setFood([]);
    return;
  }

  const foodRef = ref(database, "food/");
  try {
    const snapshot = await get(foodRef);
    if (snapshot.exists()) {
      const foodArray = Object.entries(snapshot.val()).map(([idd, data]) => ({
        idd,
        ...data,
      }));
      const filteredFoods = foodArray.filter((food) =>
        food.idd.toLowerCase().includes(debouncedSearch.toLowerCase())
      );
      setFood(filteredFoods);
      setCheckedStates(Array(foodArray.length).fill(false));
    } else {
      console.log("No data available");
    }
  } catch (error) {
    console.error("Error retrieving data:", error);
  }
};

/** The code snippet `export const debouncedSearchFunction = debounce((searchValue) => {
    setDebouncedSearch(searchValue);
  }, 500);` is defining a function named `debouncedSearchFunction` that utilizes the `debounce`
function from the lodash library. */
export const debouncedSearchFunction = debounce((searchValue) => {
  setDebouncedSearch(searchValue);
}, 500);

/** The line `const cachedFoods = useMemo(() => foods, [foods]);` is utilizing the `useMemo` hook in
React to memoize the `foods` state variable. */
export const cachedFoods = useMemo(() => foods, [foods]);

/**
 * The handleFoodSearch function updates the searchedFood state with the value entered in an input
 * field.
 * @param event - The `event` parameter in the `handleFoodSearch` function is an object that represents
 * the event that occurred, such as a user input event. In this case, it is capturing the value entered
 * by the user in an input field.
 */
export const handleFoodSearch = (event) => {
  const enteredFood = event.target.value;
  setSearchedFood(enteredFood);
};

/**
 * The function `handleToggleChange` toggles the boolean value at a specific index in an array and
 * updates the state with the new array.
 * @param index - The `index` parameter in the `handleToggleChange` function represents the index of
 * the item in the `checkedStates` array that needs to be toggled.
 */
export const handleToggleChange = (index) => {
  const newCheckedStates = [...checkedStates];
  newCheckedStates[index] = !newCheckedStates[index];
  setCheckedStates(newCheckedStates);
};