Repository navigation
Save the shopping list progress with the menu - #80
Conversation
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
left a comment
There was a problem hiding this comment.
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.
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
left a comment
There was a problem hiding this comment.
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.
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>
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.
shoppingProgressfield onMultiWeekMenugoes into the.tsmfile. Old files without it still load. Bad values are dropped one at a time, with a warning..tsmfile through the same flow as the menu page Save button, so "Open last save" at startup restores the progress.Small fixes found on the way:
SaveMenuButtonawaits 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.heroTag: null.Docs:
AGENTS.md(Persistence section, domain table, test count) and ADRs 0003, 0008, 0015, 0016.Test plan
flutter analyze, and all 1121 tests pass (48 new)🤖 Generated with Claude Code