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:
| Role | Default |
|---|---|
| Guest | Off |
| Member | On |
| Power User | On |
| Admin | On |
| Owner | On |
The permission controls both interface visibility and server-side access:
- the account menu shows My Kitchen only when allowed;
/my-kitchen.phpenforces the permission;/shopping-list.phpenforces the permission;/print-shopping-list.phpenforces 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:
- adds all currently selected recipes to their chosen days;
- applies planned servings where supplied;
- regenerates the shopping list from the full week's plan;
- 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:
- requires a logged-in user;
- requires
meal_planner.use; - validates CSRF;
- validates that the recipe is published;
- validates that the selected date belongs to the current Sunday-Saturday week;
- adds the recipe to that day;
- refreshes an existing shopping list for that week;
- 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.
