Skip to content

Save the shopping list progress with the menu - #80

Merged
guplem merged 11 commits into
mainfrom
79-save-shopping-progress
Oct 3, 2026
Merged

guplem merged 11 commits into
mainfrom
79-save-shopping-progress

Conversation

@guplem

@guplem guplem commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Summary

Closes #79

The shopping page now keeps what you typed. You can leave it, come back, or restart the app, and the "Owned" fields and the "Try to make one trip" switch show the same values.

  • Stored in the menu file. A new optional shoppingProgress field on MultiWeekMenu goes into the .tsm file. Old files without it still load. Bad values are dropped one at a time, with a warning.
  • Survives navigation. The shopping page sends each change back to the menu page at once (a callback), so any way of leaving keeps it, the system back button too. Both "Regenerate" and "Generate Menu" keep it, because the stock at home does not depend on the menu.
  • New Save button on the shopping page. It saves the .tsm file through the same flow as the menu page Save button, so "Open last save" at startup restores the progress.
  • Product counts are keyed by product link plus unit, not by list index. The index changes when products are added or reordered. The link alone is not enough: 27 bundled ingredients have a grams and a pieces product with the same link.
  • Stale entries are cleaned. Saved stock of a deleted ingredient or product is dropped at the next change, so the file stays clean.

Small fixes found on the way:

  • Failed saves now show a message. A shared SaveMenuButton awaits the save, logs the error and shows a SnackBar (a short message bar), on both pages. Before, a failed menu save looked like a success.
  • Hero tag crash. Two buttons with the same default hero tag (the tag that Flutter uses for its page-change animation) made debug builds throw on page change. Both pages now set heroTag: null.
  • Unit dropdown crash. A saved unit that the ingredient no longer offers would crash the unit dropdown. The page now drops that amount with a warning.

Docs: AGENTS.md (Persistence section, domain table, test count) and ADRs 0003, 0008, 0015, 0016.

Test plan

  • Format check, flutter analyze, and all 1121 tests pass (48 new)
  • Manual: fill some Owned fields, go back to the menu, open the shopping list again
  • Manual: save from the shopping page, restart, pick "Open last save", open the shopping list

🤖 Generated with Claude Code

guplem and others added 4 commits October 3, 2026 16:06
The Export and Save buttons of the menu page both used the default
FloatingActionButton hero tag. Flutter asserts that the tags of one page
are unique, so each push to the shopping page threw in debug builds. A
null hero tag turns off the animation and the assertion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the unit list of the header "Owned" dropdown out of the
ShoppingIngredient state into the pure function availableOwnedUnits.
The shopping page needs the same list to check a saved owned unit
before it restores it, because the dropdown asserts that its value is
one of its items.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the ShoppingProgress model and the optional
MultiWeekMenu.shoppingProgress field, so a .tsm file can keep what the
user typed on the shopping page: the header owned amounts, the owned
product counts and the "Try to make one trip" switch.

The file keys each product count by the product link, because the
product index changes when the user edits the products. The model
converts between link keys and the index keys of the page, drops a
link that matches no product, and keeps the stock of ingredients that
the current menu does not need.

The JSON leaves out an empty progress. fromJsonLenient drops each bad
value with a warning, so one bad value never costs the whole menu file.

Refs #79

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shopping page now restores the owned stock and the trip switch from
the menu, and reports each change to MenuPage through a callback.
MenuPage stores it through its menu setter, so the MenuProvider mirror
stays in sync and every way to leave the page, the system back button
too, keeps the progress. A new Save button saves the .tsm with the
progress on screen, through the same dialog as the menu page. A menu
regeneration keeps the progress, like it keeps the first day.

Closes #79

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@guplem guplem added the waiting-for-human-check No human has verified this yet -- direct AI output label Oct 3, 2026
@guplem guplem self-assigned this Oct 3, 2026

@guplem guplem left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid structure: the progress lives on MultiWeekMenu, parsing is lenient value by value, the callback keeps the MenuProvider mirror in sync, and every way of leaving the page keeps the progress. All five acceptance criteria of #79 trace end to end.

[Required] One bug blocks merge: product counts are keyed by link, but 27 bundled ingredients have a grams product and a pieces product with the same link. Counts collide on save and land on the wrong product on load (inline comment).

[Suggestion] Docs: ADR 0008 does not mention shoppingProgress; ADR 0015 still calls the switch an internal flag; ADR 0016 should say the deletion cascade leaves stale progress entries on purpose (the load drops them). ADR 0003 should also list the dropped-unit rule and say why the JSON is hand-written (the record type cannot go through json_serializable), and that the lenient parse sits in the model, unlike startDate. The shopping/ row of the AGENTS.md domain table should list ShoppingProgress.

[Suggestion] Generate from the configuration page drops the stock: only the regenerate button on MenuPage keeps shoppingProgress. "Generate Menu" in menu_configuration_page.dart starts with none, although the stock at home does not depend on the menu. Carry it over from MenuProvider's current menu.

[Suggestion] Tests: no widget test restores or drops a saved "packs" amount; the restore test finds TextField "2" anywhere, not on a given product row; the back-navigation test only toggles the switch.

Comment thread menu_management/lib/shopping/shopping_progress.dart Outdated
Comment thread menu_management/lib/shopping/shopping_page.dart Outdated
Comment thread menu_management/lib/shopping/shopping_page.dart
Comment thread menu_management/lib/shopping/shopping_page.dart Outdated
Comment thread menu_management/lib/shopping/shopping_page.dart Outdated
Comment thread menu_management/lib/shopping/shopping_progress.dart Outdated
guplem and others added 4 commits October 3, 2026 16:20
The menu page Save button dropped the Future of Persistency.saveMenu,
and saveMenu catches no error. A failed save dialog or file write
became an unhandled async error, and the user saw nothing.

The new SaveMenuButton awaits the save, logs the error with
Debug.logError, and shows a snackbar. The menu page uses it now, and
the shopping page uses it next, so both Save buttons behave alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only the regenerate button of MenuPage kept the shopping progress.
"Generate Menu" on the configuration page started with none, although
the stock at home does not depend on the menu. It now copies the
progress of the active menu from the MenuProvider mirror.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
27 bundled ingredients have a grams product and a pieces product with
the same store link. With link-only keys, the two counts collided on
save, and on load a pieces count landed on the grams product, where
OwnedStock still subtracted it.

Each count is now saved as one object with its link, unit and count,
and the load matches both. When two products share link and unit, the
first of them gets the count. The shopping page also drops, with a
warning, a header amount that product rows now replace and the amount
of an ingredient that no longer exists. Widget tests cover a shared
link, a "packs" amount, and the owned count on the back navigation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0003 gains the link plus unit key, the dropped-value rules, and
why the JSON is hand-written and parsed in the model. ADR 0008 lists
the optional progress, ADR 0015 says where the trip switch is stored,
and ADR 0016 says why the delete cascade leaves stale progress.
AGENTS.md lists ShoppingProgress and the new test count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@guplem guplem left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review: all six earlier findings and the summary items are fixed. Counts are now keyed by link plus unit, failed saves show a message, "Generate Menu" keeps the stock, and the docs cover the new field. Format, analyze and all 1119 tests pass.

One new issue: saved stock of a deleted ingredient is never removed, and ADR 0016 says it is (inline). One test nitpick.

Comment thread menu_management/lib/shopping/shopping_progress.dart Outdated
Comment thread menu_management/test/menu_page_test.dart Outdated
guplem and others added 3 commits October 3, 2026 16:24
fromPageState copied every previous entry of an ingredient that the
page did not show. A deleted ingredient never shows on the page, so
its entry stayed in the .tsm file forever, and the open logged the same
warning each time. It now drops a previous entry whose ingredient no
longer exists, and a previous count whose link and unit match no
product. Entries of existing ingredients outside the current menu stay.

ADR 0003 and ADR 0016 now describe when the page drops such entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The back-navigation test found the text "2" in any field of the page.
It now looks only inside the product row of the rice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The grams product and the pieces product of one store item share a
link, so saved data must identify a product by link plus unit. The
review of PR #80 caught this collision late; the gotcha keeps the
next change from repeating it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@guplem
guplem enabled auto-merge October 3, 2026 14:38
@guplem
guplem merged commit fc24f60 into main Oct 3, 2026
1 check passed
@guplem
guplem deleted the 79-save-shopping-progress branch October 3, 2026 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-for-human-check No human has verified this yet -- direct AI output

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Save the shopping list progress so I can stop mid-way and continue later

1 participant