Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Set Up

Sync Data from MDM

The first change from our current implementation is to expect all nutrition info to come from MDM. Instead of calculating a per-weight or per-volume value for a given nutrient, that data will be stored in Sanity and will come directly from MDM. How the data is synced is outside the scope of this design review.

The relevant part to understand is that there will now be a row of data keyed by calories and a new row of data keyed by caloriesPer100. If the app is configured to show a table of "Per 100g/100ml" values, it will source that information from the new rows of data with a key like caloriesPer100.

New Sanity Schemas

The following subsections will roughly describe new schemas to support Nutrition 2.0. Keep in mind that the goal of the new configuration will be to give franchisee owners the ability to configure nutrition information in a way that meets legal requirements in their country.

Nutrition Feature

The nutrition 2.0 configuration object will live in a feature singleton. Using a feature singleton makes it easy to cache the information and manage the state.

The singleton will roughly look like:

Code Block
{
  title: 'Feature Nutrition 2.0',
  name: 'featureNutrition2.0',
  description: 'App-wide nutrition information configuration',
  type: 'document',
  fields: [
    {
      name: 'nutritionTables',
      title: 'Nutrition Tables',
      description: 'The list of tables that contain per-item nutrition information',
      type: 'array',
      of: [{ type: 'nutritionTable' }],
    }
  ]
}
Nutrition Table

In the UK, the nutrition info modal displays two tables: "Per serving" and "Per 100g or ml". In the US, we only display per-serving nutrition info. We want to support one or more tables. Each table will be represented by an object that looks like this:

Code Block
{
  title: 'Nutrition Table',
  name: 'nutritionTable',
  description: 'Nutrition items (or groups of nutrition items) to show in a given nutrition table',
  type: 'object',
  fields: [
    {
      title: 'Title',
      description: 'The title of the nutrition table, e.g. Per Serving',
      name: 'title',
      type: 'localeString',
      validation: (Rule) => Rule.required(),
    },
    {
      title: 'Items',
      description: 'The items or groups of items that will be shown the table',
      name: 'items',
      type: 'array',
      of: [{ type: 'nutritionItemGroup' }, { type: 'nutritionItem' }],
      validation: (Rule) => Rule.required().min(1),
    }
  ]
}
Nutrition Item Group

We need a way to tell the UI that items should be grouped together. Imagine a UI like:

Code Block
Fat             12g
  Trans Fat      2g
  Saturated Fat 10g

In this case, we want Trans and Saturated Fats to be grouped visually under the Fat item. This is where Nutrition Item Group comes in. It will have an object that looks like:

Code Block
{
  title: 'Nutrition Item Group',
  name: 'nutritionItemGroup',
  description: 'Group of nutrition items that will be displayed displayed as a main item with sub-items',
  type: 'object',
  fields: [
    {
      title: 'Main Item',
      description: 'The main nutrition item',
      name: 'mainItem',
      type: 'nutritionItem',
      validation: (Rule) => Rule.required()
    },
    {
      title: 'Line Items',
      description: 'The items that will be displayed below the main item',
      name: 'lineItems',
      type: 'array',
      of: [{ type: 'nutritionItem' }],
      validation: (Rule) => Rule.required().min(1),
    },
  ]
}
Nutrition Item

The final piece of the puzzle is the Nutrition Item. This schema must support:

  • displaying additional nutrition items side-by-side

  • localizing units

  • localizing names

The schema will look like:

Code Block
{
  title: 'Nutrition Item',
  name: 'nutritionItem',
  description: 'Item that will be displayed as a row in a nutrition table',
  type: 'object',
  fields: [
    {
      title: 'Nutrition Key',
      name: 'nutritionKey',
      description: 'The key to the corresponding row in the nutrition data per item, e.g. calories',
      type: 'string',
      options: {
        // nutrition constant exported from cms/schemas/constants
        list: nutrition.map((n) => ({
          title: n.title,
          value: n.name,
        })),
        layout: 'dropdown',
      },
      validation: (Rule) => Rule.required(),
    },
    {
      title: 'Name',
      name: 'name',
      description: 'The localized name of the nutrition item',
      type: 'localeString',
      validation: (Rule) => Rule.required(),
    },
    {
      title: 'Unit',
      name: 'unit',
      description: 'The localized unit of measurement for the nutrition item. Must accurately reflect the unit used by the data synced to Sanity from MDM',
      type: 'localeString',
      validation: (Rule) => Rule.required(),
    },
    {
      title: 'Additional Items',
      name: 'additionalItems',
      description: 'The nutrition items that should be displayed inline after the "parent" item, e.g. "250 kcal / 1046 KJ" where kilojoules is in the additionalItems array',
      type: 'array',
      of: [{ type: 'nutritionItem' }],
    },
  ]
}