Display Macro Chart

Display a Macronutrient chart using nutrients from PassioFoodItem and PassioRecipe.

Require victory-native and react-native-svg npm package to display the chart.

yarn install victory-native

yarn install react-native-svg

Example

Displaying the nutritional values for calories, carbs, proteins, and fats using the mockFood data, which includes logged dates for the respective food entries.

Retrieve mock data from this.

pageMockFood

Obtain the utility function for fetching nutrition from the provided page.

pageUtils PassioNutrient

MacrosNutrient

import React from 'react';
import { Text, View, ScrollView } from 'react-native';
import {
  VictoryAxis,
  VictoryBar,
  VictoryChart,
  VictoryLabel,
  VictoryTheme,
} from 'victory-native';
import { useMacrosNutrients } from './useMacrosNutrients';
import {
  mergeAndSumMacroChartData,
  type NutrientsTypes,
} from '../../../utils/PassioNutrient';

// Color mapping for each nutrient type
const NutrientColors = {
  calories: '#FF4D61',
  fat: '#00D795',
  protein: '#0899FF',
  carbs: '#00D795',
};

const MacrosNutrient = () => {
  // Fetch nutrient data using custom hook
  const { passioIDAttributesData, macros } = useMacrosNutrients();

  // Function to render individual nutrient chart
  const renderNutrientChart = (item: NutrientsTypes) => (
    <View key={item}>
      {/* Nutrient type label */}
      <Text style={{ marginVertical: 8, alignSelf: 'center' }}>{item}</Text>

      {/* Victory Chart for the nutrient */}
      <VictoryChart theme={VictoryTheme.material} height={200}>
        {/* X-axis configuration */}
        <VictoryAxis
          style={{
            ticks: { stroke: 'transparent' },
            grid: { stroke: 'none' },
            axis: { stroke: '#F7F7F7' },
            tickLabels: { fill: '#333333', fontSize: 14 },
            axisLabel: {},
          }}
        />

        {/* Y-axis configuration */}
        <VictoryAxis
          dependentAxis
          fixLabelOverlap
          style={{
            ticks: { stroke: 'transparent' },
            axis: { stroke: 'none' },
            grid: { stroke: '#F7F7F7' },
            axisLabel: { padding: 0 },
            tickLabels: { fill: '#333333', fontSize: 14 },
          }}
        />

        {/* Victory Bar chart for the nutrient */}
        <VictoryBar
          cornerRadius={8}
          labelComponent={
            <VictoryLabel
              text={({ datum }) => Math.round(datum.value)}
              dy={-10}
            />
          }
          alignment="middle"
          style={{
            data: {
              borderTop: 24,
              width: 20,
              fill: NutrientColors[item],
            },
          }}
          data={mergeAndSumMacroChartData(
            passioIDAttributesData.filter((data) => data.type === item)
          )}
          x={(d) => d.label}
          y="value"
        />
      </VictoryChart>
    </View>
  );

  // Render the component
  return (
    <ScrollView>
      <View>
        {/* Iterate over nutrient types and render charts */}
        {macros.map(renderNutrientChart)}
      </View>
    </ScrollView>
  );
};

export default MacrosNutrient;

useMacrosNutrients

import { useEffect, useMemo, useState } from 'react';
import {
  getNutrientsFromAttribute,
  type NutrientsTypes,
} from '../../../utils/PassioNutrient';
import { PassioSDK } from '@passiolife/nutritionai-react-native-sdk-v2';
import { DateTime } from 'luxon';
import { mockFood } from './mockFood';

export interface MacroChartData {
  label: string;
  value: number;
  type: NutrientsTypes;
}

export const useMacrosNutrients = () => {
  // State to store nutrient data

  const macros: NutrientsTypes[] = useMemo(() => {
    return ['calories', 'carbs', 'fat', 'protein'];
  }, []);

  const [passioIDAttributesData, setPassioIDAttributesData] = useState<
    MacroChartData[]
  >([]);

  // Effect to fetch and process nutrient data on component mount
  useEffect(() => {
    // Temporary array to store processed nutrient data
    const data: MacroChartData[] = [];

    async function fetchData() {
      // Create a copy of mockFood array to avoid modifying the original
      const copyFood = [...mockFood];

      // Fetch attributes for each PassioID and process nutrient data
      await Promise.all(
        copyFood.map(async (foodLog) => {
          // Get attributes for a PassioID using PassioSDK
          const attribute = await PassioSDK.getAttributesForPassioID(
            foodLog.passioID
          );

          // If attributes are available, process the nutrients
          if (attribute) {
            // Get nutrients from the attribute for specific nutrient types
            getNutrientsFromAttribute(attribute, macros).forEach((item) => {
              // Convert timestamp to a formatted date and add the nutrient data to the array
              data.push({
                label: DateTime.fromJSDate(
                  new Date(foodLog.eventTimestamp)
                ).toFormat('dd/MM'),
                value: item.unitMass?.value ?? 0,
                type: item.name,
              });
            });
          }
        })
      );

      // Update the state with the processed nutrient data
      setPassioIDAttributesData(data);
    }

    // Call the fetchData function
    fetchData();
  }, [macros]); // Empty dependency array to ensure the effect runs only once on mount

  // Return the macros constant and the processed nutrient data
  return {
    macros,
    passioIDAttributesData,
  };
};

Result

Last updated