MealPlan

Display a meal plan that includes breakfast, lunch, snack, and dinner, specifying the food items and their corresponding recipes.

Example

MealPlanScreen

/**
MealPlanScreen Component
Displays a SectionList of meal plans, including MealPlan and MealMenu items.

@component
@example

*/
import React from 'react';
import { Image, View, StyleSheet } from 'react-native';
import { Text } from '../../../components';
import {
  convertToGroup,
  isMealMenu,
  isMealPlan,
  type MealMenu,
  type MealPlan,
} from './data/data';
import { mealPlanBalanced } from './data/mealData';
import {
  IconSize,
  PassioIDEntityType,
  PassioIconView,
} from '@passiolife/nutritionai-react-native-sdk-v2';
import { SectionList } from 'react-native';

const MealPlanScreen = () => {
  const renderItem = ({ item }: { item: MealPlan | MealMenu }) => {
    return (
      <View style={styles.cardContainer}>
        {isMealPlan(item) && (
          <>
            <View style={styles.rowContainer}>
              <PassioIconView
                config={{
                  passioID: item.passioID,
                  iconSize: IconSize.PX180,
                  passioIDEntityType: PassioIDEntityType.item,
                }}
                style={styles.icon}
              />
              <View style={styles.infoContainer}>
                <Text style={styles.nameText}>{item.name}</Text>
                <Text style={styles.quantityText}>
                  {item.unitQuantity.replaceAll('%', ' ')}
                </Text>
              </View>
            </View>
          </>
        )}

        {isMealMenu(item) && (
          <View style={styles.rowContainer}>
            <Image
              source={require('../../../assets/icons/default_recipe.jpg')}
              style={styles.icon}
            />
            <View style={styles.infoContainer}>
              <Text style={styles.nameText}>{'Recipe of ' + item.name}</Text>
              <Text style={styles.quantityText}>
                {'Total Serving ' + item.totalServings}
              </Text>
              <Text style={styles.quantityText}>
                {'Total Ingredients ' + item.ingredients.length}
              </Text>
            </View>
          </View>
        )}
      </View>
    );
  };

  return (
    <View style={styles.bodyContainer}>
      <SectionList
        sections={convertToGroup(mealPlanBalanced)}
        renderSectionHeader={({ section: { title } }) => (
          <Text style={styles.itemHeader}>{title}</Text>
        )}
        renderItem={renderItem}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  bodyContainer: {
    backgroundColor: '#f0f0f0',
    flex: 1, // Ensure that the container takes the full height
  },
  cardContainer: {
    backgroundColor: 'white',
    padding: 16,
    marginVertical: 8,
    marginHorizontal: 16,
    borderRadius: 8,
  },
  rowContainer: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  itemHeader: {
    marginHorizontal: 16,
    marginVertical: 16,
    fontSize: 16,
    fontWeight: '600',
    textTransform: 'capitalize',
  },
  icon: {
    height: 48,
    width: 48,
    borderRadius: 24, // Optional: Add borderRadius to round the icon
    marginRight: 16, // Add some spacing between icon and text
  },
  infoContainer: {
    flex: 1,
  },
  nameText: {
    fontSize: 16,
    fontWeight: '500', // Adjusted fontWeight for better hierarchy
  },
  quantityText: {
    marginTop: 10,
    fontSize: 14,
  },
});

export default MealPlanScreen;
import type { PassioID } from '@passiolife/nutritionai-react-native-sdk-v2';

export interface MealPlan {
  day: number;
  name: string;
  passioID: PassioID;
  servingSize: number;
  type: MealLabel;
  unitQuantity: string;
}
export interface Ingredient {
  passioId: PassioID;
  quantity: number;
  servingSize: number;
  unit: string;
}

export interface MealMenu {
  ingredients: MealPlan[];
  name: string;
  totalServings: number;
  uuid: string;
}

export enum MealLabel {
  BREAKFAST = 'breakfast',
  LUNCH = 'lunch',
  DINNER = 'dinner',
  SNACK = 'snack',
} 

export interface MealPlanFoodGroup {
  data: (MealPlan | MealMenu)[];
  title: MealLabel;
}

Mock Data: Meal Plan Balanced

/**
 * Mock Data: Meal Plan Balanced
 *
 * Represents a collection of meal plans and menus for a balanced diet.
 *
 * @constant
 * @type {(MealPlan | MealMenu)[]}
 * @example
 * const mealPlanBalanced: (MealPlan | MealMenu)[] = [
 *   // ... (sample data entries)
 * ];
 */
 
 import { MealLabel, type MealMenu, type MealPlan } from './data';

export const mealPlanBalanced: (MealPlan | MealMenu)[] = [
  {
    day: 1,
    name: 'coffee with milk',
    passioID: 'BEV0037',
    servingSize: 1,
    type: MealLabel.BREAKFAST,
    unitQuantity: '1%serving%(270.5 g) ',
  },
  {
    day: 1,
    name: 'cooked oatmeal with berries',
    passioID: '1001608',
    servingSize: 1,
    type: MealLabel.BREAKFAST,
    unitQuantity: '1%cup%(67.0 g) ',
  },
  {
    day: 1,
    name: 'whole wheat bread with butter',
    passioID: 'REC000085',
    servingSize: 1,
    type: MealLabel.BREAKFAST,
    unitQuantity: '1%serving%(64.4 g) ',
  },
  {
    ingredients: [
      {
        day: 1,
        name: 'greek yogurt',
        passioID: 'DAI0115',
        servingSize: 1,
        type: MealLabel.LUNCH,
        unitQuantity: '1%cup%(245.0 g) ',
      },
      {
        day: 1,
        name: 'strawberries',
        passioID: 'VEG0040',
        servingSize: 1,
        type: MealLabel.LUNCH,
        unitQuantity: '1%cup, whole%(144.0 g) ',
      },
    ],
    name: 'Strawberry yogurt',
    totalServings: 1,
    uuid: 'default-uuid',
  },
  {
    day: 1,
    name: 'turkey wrap',
    passioID: 'PRE0283',
    servingSize: 1,
    type: MealLabel.LUNCH,
    unitQuantity: '1%serving%(150.0 g) ',
  },
  {
    day: 1,
    name: 'herbal tea',
    passioID: 'BEV0150',
    servingSize: 1,
    type: MealLabel.LUNCH,
    unitQuantity: '1%6 fl oz%(178.0 g) ',
  },
  {
    ingredients: [
      {
        day: 1,
        name: 'whole beef roast',
        passioID: 'MEA0221',
        servingSize: 1,
        type: MealLabel.DINNER,
        unitQuantity: '3%oz%(84.99 g) ',
      },
      {
        day: 1,
        name: 'roasted cauliflower',
        passioID: 'VEG0365',
        servingSize: 1,
        type: MealLabel.DINNER,
        unitQuantity: '1%cup%(140.0 g) ',
      },
      {
        day: 1,
        name: 'cooked brown rice',
        passioID: 'GRA0003',
        servingSize: 1,
        type: MealLabel.DINNER,
        unitQuantity: '0.5%cup, cooked%(97.5 g) ',
      },
    ],
    name: 'Beef roast with cauliflower',
    totalServings: 1,
    uuid: 'default-uuid',
  },
  {
    day: 1,
    name: 'apples',
    passioID: 'VEG0018',
    servingSize: 1,
    type: MealLabel.SNACK,
    unitQuantity: '1%medium%(172.0 g) ',
  },
  {
    day: 1,
    name: 'cucumber salad',
    passioID: 'PRE0074',
    servingSize: 1,
    type: MealLabel.SNACK,
    unitQuantity: '2%cup%(318.0 g) ',
  },
  {
    day: 1,
    name: 'string cheese',
    passioID: 'DAI0125',
    servingSize: 1,
    type: MealLabel.SNACK,
    unitQuantity: '1%piece%(28.0 g) ',
  },
  {
    day: 1,
    name: 'cashews',
    passioID: 'NUT0020',
    servingSize: 1,
    type: MealLabel.SNACK,
    unitQuantity: '1%oz%(28.35 g) ',
  },
];

Utils Methods:

// Type guards

/**
 * Type guard for MealPlan.
 *
 * @function
 * @param {MealPlan | MealMenu} item - The item to be checked.
 * @returns {item is MealPlan} - True if the item is a MealPlan, false otherwise.
 */
export function isMealPlan(item: MealPlan | MealMenu): item is MealPlan {
  return 'unitQuantity' in item; // Replace with an actual property check
}

/**
 * Type guard for MealMenu.
 *
 * @function
 * @param {MealPlan | MealMenu} item - The item to be checked.
 * @returns {item is MealMenu} - True if the item is a MealMenu, false otherwise.
 */
export function isMealMenu(item: MealPlan | MealMenu): item is MealMenu {
  return 'ingredients' in item; // Replace with an actual property check
}

 /**
   * Converts an array of meals into a grouped structure based on MealLabel.
   *
   * @function
   * @param {MealPlan[] | MealMenu[]} meals - The array of meals to be converted.
   * @returns {MealPlanFoodGroup[]} - The grouped structure of meals.
   */
   
export const convertToGroup = (
  meals: (MealPlan | MealMenu)[]
): MealPlanFoodGroup[] => {
  const breakFast = [
    MealLabel.BREAKFAST,
    MealLabel.LUNCH,
    MealLabel.SNACK,
    MealLabel.DINNER,
  ];
  return breakFast
    .map((item) => {
      return {
        data: meals.filter((mealMenu) =>
          isMealMenu(mealMenu)
            ? mealMenu.ingredients[0]?.type === item
            : mealMenu.type === item
        ),
        title: item,
      };
    })
    .filter((item) => item.data.length > 0);
};

Result

Last updated