/**
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
*
* 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) ',
},
];
// 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);
};