Documentation / My Kitchen

My Kitchen

Updated: 2026-09-01

My Kitchen is the signed-in user's weekly meal-planning and shopping-list workspace.

Permission

Feature permission:

meal_planner.use

Default role assignments:

RoleDefault
GuestOff
MemberOn
Power UserOn
AdminOn
OwnerOn

The permission controls both interface visibility and server-side access:

  • the account menu shows My Kitchen only when allowed;
  • /my-kitchen.php enforces the permission;
  • /shopping-list.php enforces the permission;
  • /print-shopping-list.php enforces the permission;
  • recipe pages show Add to Meal Plan only when allowed.

Changing/hiding the menu control is not a security substitute for the route checks.

Weekly meal planner

Weeks run from Sunday through Saturday.

For each day a user can:

  • leave the day empty;
  • add one or more recipes;
  • optionally enter planned servings for each newly selected recipe;
  • change planned servings later;
  • remove a planned recipe later.

Previous/next week navigation keeps plans independent by week.

Selecting several recipes at once

The week page uses one combined weekly selection form. Recipes can be chosen for several days before building the list.

There is intentionally no separate Save Selected Recipes button. Pending day selections and optional planned-servings values are remembered in browser localStorage, keyed by week, so leaving and returning to the page does not normally discard the selections.

Build / Refresh Shopping List performs the commit/build action:

  1. adds all currently selected recipes to their chosen days;
  2. applies planned servings where supplied;
  3. regenerates the shopping list from the full week's plan;
  4. opens the Shopping List tab.

If the user clicks the Shopping List tab while pending selections exist, those selections are submitted and the shopping list is built first.

Browser-local draft storage is convenience only. If local storage is disabled or cleared, the planner still works, but unsubmitted selections cannot be restored.

Add from a recipe page

Authorized users see an Add to Meal Plan control on normal recipe pages. The dropdown lists Sunday through Saturday for the current week, including dates.

Submitting the control:

  1. requires a logged-in user;
  2. requires meal_planner.use;
  3. validates CSRF;
  4. validates that the recipe is published;
  5. validates that the selected date belongs to the current Sunday-Saturday week;
  6. adds the recipe to that day;
  7. refreshes an existing shopping list for that week;
  8. returns to the recipe page with confirmation.

The same recipe can be planned more than once when the user intentionally adds it more than once.

Clear Meal Plan

Clear Meal Plan removes all recipes from the selected week after confirmation and refreshes the existing shopping list so stale ingredients are removed. Other weeks are unaffected.

Planned servings

Each planned recipe can have a planned serving count.

When generating shopping requirements, ingredient quantities are scaled only when the recipe's stored base servings can be interpreted safely. Simple forms such as 4, 4 servings, or Serves 4 can scale. Ambiguous servings such as 4-6 servings are not guessed and retain a 1x ingredient factor.

Once a shopping list exists, adding/removing planned recipes or changing planned servings refreshes that list automatically. Rebuild from Meal Plan remains available as an explicit recovery/force-refresh action.

Building and consolidating the shopping list

The shopping list is organized around what the user actually buys rather than around individual recipe ingredient rows.

High-confidence equivalents are consolidated. For example:

1 medium red onion, diced
1 red onion, sliced

can become one grocery purchase row while retaining the original recipe wording in the source breakdown.

Grocery identity

Preparation and ordinary size wording do not create duplicate grocery rows when the underlying item is the same. Examples such as medium-size red onion, diced and red onions, sliced share the grocery identity red onion.

Distinct groceries remain distinct unless there is a deliberate shopping rule. Green onions/scallions remain separate from bulb onions.

A small set of unambiguous aliases is normalized, such as scallion/green onion and confectioners sugar/powdered sugar.

Bulb-onion varieties purchased as individual onions can share one Onion purchase row when compatible. The row retains a visible variety breakdown so red/yellow/white/sweet requirements are not lost.

Compatible measurement conversion

Compatible units can be converted before totaling:

  • tsp / tbsp / cup / ml / L;
  • oz / lb / g / kg.

Examples:

2 tbsp olive oil + 1/4 cup olive oil = 6 tbsp olive oil
1 lb potatoes + 8 oz potatoes = 1 1/2 lb potatoes

The generator does not invent ingredient-specific density or count conversions. 1 onion and 1 cup chopped onion remain separate unless the recipe itself supplies a trustworthy purchase hint such as (from 1 onion).

Quantity ranges

When a raw ingredient gives a true numeric range, the shopping requirement uses the larger numeric endpoint so the shopper does not come up short. This includes fractional ranges and ranges written in descending textual order.

Mixed-number notation must remain distinguishable from a range.

Package-aware grocery quantities

When a recipe explicitly supplies a package size, the shopping list preserves that package rather than converting it into an unnatural bulk measurement.

Examples:

1 (14.5 oz) can diced tomatoes -> 1 x 14.5 oz can
2 cans (14 oz) diced tomatoes -> 2 x 14 oz cans

A deliberately narrow set of unmistakably canned legacy ingredients can also be interpreted as a can when the source provides only the net weight, for example:

28 oz Pork and Beans -> 1 x 28 oz can

Package counts scale with planned servings while package size remains fixed. Ordinary weighted ingredients are never guessed to be packages.

Grocery-store grouping

Shopping items are grouped into store-oriented sections to make the list easier to use while shopping. Current groups include:

  • Produce;
  • Bakery & Bread;
  • Meat & Seafood;
  • Dairy & Eggs;
  • Canned & Jarred;
  • Dry Goods & Baking;
  • Spices & Seasonings;
  • Condiments & Sauces;
  • Frozen;
  • Beverages;
  • Other.

Items remain alphabetized within each section. The same grouping is used in the editor, phone shopping view, and print output.

Classification is conservative and can be refined through real use; unknown items fall back safely rather than being forced into a weak category.

Shopping editor columns

The normal editor uses a compact sticky header with these columns:

  • Bought — shopping-progress state; mark after the item has been purchased/handled;
  • Item — consolidated grocery item name; editable without changing the recipe itself;
  • Recipe Total — total amount required by all planned recipes after serving scaling and safe consolidation;
  • On Hand — quantity already available for ordinary numeric items;
  • Buy — amount to purchase;
  • Unit — purchase unit/package such as each, oz, lb, can, bottle, or container;
  • Skip — exclude the item from this shopping trip without deleting the recipe requirement;
  • Pantry — treat the item as a pantry staple rather than precise tracked inventory.

Each column heading has a native hover tooltip explaining its purpose.

The column header page-scrolls normally until it reaches the site header, then remains sticky beneath it.

Number-input values use decimal strings acceptable to HTML number controls. Human-facing source text can still use fractions.

Recipe Total, On Hand, and Buy

Shopping arithmetic keeps three concepts separate:

  • Recipe Total — combined amount required by the planned recipes;
  • On Hand — what the user already has when numeric inventory makes sense;
  • Buy — purchase quantity, which may use a different purchase unit.

When Buy has not been manually overridden, ordinary numeric items use:

max(0, Recipe Total - On Hand)

Untouched On Hand fields display blank rather than showing a misleading entered zero.

Recipe Total, On Hand, Buy, item name, and purchase unit auto-save field by field.

Pantry staples

Pantry mode is intentionally lightweight; it is not a full pantry inventory database.

It is intended for things normally kept around and usually not inventoried by teaspoons or cups, such as flour, salt, pepper, common spices, oil, vinegar, sugar, and similar staples.

When Pantry is enabled:

  • On Hand displays - instead of requiring a numeric quantity;
  • the item normally defaults to buying one container/package when it is needed;
  • the purchase unit remains editable, so 1 jar, 1 bottle, 1 lb bag, etc. can be entered;
  • use Skip when enough is already available at home.

The user can turn Pantry mode on or off for any item, and explicit decisions persist through normal shopping-list regeneration where the item can be matched safely.

Bought versus Skip

Bought and Skip are deliberately separate states:

  • Bought means the item belonged on this shopping trip and has now been purchased/handled. It is primarily a progress/checklist state.
  • Skip means the item should not be purchased on this shopping trip, for example because enough is already available, a substitution is planned, or the item is not wanted.

Both persist through normal refresh/consolidation where matching is safe.

Whether Bought remains worthwhile should be judged through real grocery use; do not remove it solely because it resembles Skip conceptually.

Source recipes

Generated shopping items retain source-detail rows linking back to every contributing recipe.

The 1 recipe / N recipes line is expandable. For each contributing recipe it shows:

  • the recipe title as a link;
  • the complete original ingredient line from that recipe.

This makes consolidated quantities auditable without cluttering the main row.

Auto-save and persistence

Shopping-list edits auto-save one field at a time. There is no normal Save button. View/Print navigation waits for in-flight saves before leaving the page.

Refreshing or consolidating the list must not throw away ordinary shopping work.

The generator preserves user-owned state when old rows can be matched through their recipe-ingredient sources. Where meaningful this includes:

  • On Hand quantity without double-counting the same stock;
  • Recipe Total override;
  • Buy override and purchase-unit override;
  • renamed item text;
  • Pantry state;
  • Bought state;
  • Skip state;
  • manual grocery items.

When several old rows collapse into one grocery row, the consolidated item is marked Bought or Skip only if every contributing old row had that state. This prevents a partly unfinished grocery need from disappearing.

Quantity controls

Number-input spinner steps are unit-aware rather than universally 0.01.

Count/package units step by whole units. Teaspoon/tablespoon/cup/pound-style quantities use practical fractional steps, ounces use whole-ounce steps, grams use larger practical steps, and milliliters use larger practical steps. Exact decimal values can still be typed directly.

Manual grocery items

The shopping list supports non-recipe items such as milk or paper towels.

The add form asks for:

  • item name;
  • Buy quantity;
  • purchase unit.

Manual items survive meal-plan refresh. They remain user-owned rather than being reclassified from recipe data.

Phone shopping view

View Shopping List opens a phone-focused checklist using the same grocery-store grouping.

It shows the purchase quantity/unit and source recipe names while keeping the interaction compact for in-store use.

Printing

Print opens the current auto-saved shopping list in the dedicated print layout.

The print view omits:

  • Bought items;
  • skipped items;
  • numeric items whose effective Buy quantity is zero.

The print layout is compact and uses checkbox boxes suitable for paper shopping.

Dark theme and layout

My Kitchen uses the site's normal dark-theme tokens. Do not reintroduce white card fallbacks with inherited light text.

The desktop shopping editor intentionally reserves enough width for realistic quantities and purchase-unit text such as 4.75 cups and 14 oz can. At narrower widths, horizontal scrolling is preferable to clipping meaningful values.

Data model

Primary migration:

sql/migrations/2026-08-31-user-meal-plans-shopping-lists.sql

Tables:

meal_plans
meal_plan_items
shopping_lists
shopping_list_items
shopping_list_item_sources

Permission migration:

sql/migrations/2026-08-31-meal-planner-permission.sql

Shopping-mode extension:

sql/migrations/2026-08-31-shopping-list-shopping-mode.sql

Primary application files:

www/my-kitchen.php
www/shopping-list.php
www/print-shopping-list.php
www/includes/meal-planner.php
www/assets/css/my-kitchen.css
www/recipe.php
www/includes/header.php
www/includes/auth.php

Regression:

tests/meal-planner.php

Security / ownership rules

All meal plans and shopping lists are user-owned. Any update/remove action involving an item ID must verify that the item belongs to the current user's plan/list rather than trusting a posted ID.

State-changing requests require CSRF validation. Permission checks must remain server-side in addition to conditional UI rendering.

Current validation history

Initial schema/feature deployment verified:

MEAL_PLANNER=PASS
MY_KITCHEN_SCHEMA=PASS
MY_KITCHEN_DEPLOY=PASS

The dark-theme correction verified:

MY_KITCHEN_COLORS_DEPLOY=PASS

Later work added consolidated shopping identities, automatic refresh, sticky headings, pantry controls, numeric range handling, decimal-safe number inputs, source-recipe expansion, grocery-store grouping, package-aware quantities, bulk week selection, clear-week behavior, browser-local selection drafts, and explanatory column tooltips.

Before applying schema-changing upgrades, create and verify a current database backup using the supported procedure in Backup and Restore.

Possible later enhancements

My Kitchen is now in a real-use stabilization phase. Do not add features merely because they are possible. First use the current planner/shopping workflow for normal weekly planning and grocery shopping and fix concrete friction that appears.

Potential later enhancements, only if real use justifies them:

  • copy/repeat previous week;
  • meal-plan templates/history;
  • drag/drop day organization;
  • fuller pantry inventory;
  • richer recipe search/autocomplete;
  • further phone-focused polish.

Preserve the current simple weekly workflow, conservative ingredient arithmetic, grocery grouping, package-aware shopping, and shopping-state persistence while making any future changes.