Skip to content

feat(grocery): name→aisle category override rules (Phase 1) - #448

Open
plusmobileapps wants to merge 6 commits into
mainfrom
claude/cold-brew-category-parser-b47601
Open

feat(grocery): name→aisle category override rules (Phase 1)#448
plusmobileapps wants to merge 6 commits into
mainfrom
claude/cold-brew-category-parser-b47601

Conversation

@plusmobileapps

Copy link
Copy Markdown
Collaborator

What & why

The grocery list categorizes items by running the name through IngredientParser, a static keyword→GroceryCategory map. Anything it doesn't recognize falls to Other — which is exactly what happened with "cold brew". Keyword whack-a-mole doesn't scale: users buy products the built-in vocabulary will never cover, and sometimes the parser's guess is just wrong for how their store is laid out.

This adds a persistent, name-keyed rule — "always file <name> under <aisle>" — that retags every future item whose parsed name matches. Scope is limited to the 13 existing aisles; letting users create brand-new aisles (which means unwinding the GroceryCategory enum used app-wide) is deferred to Phase 2.

How it works

Resolution order when mapping a stored row to a grocery item:
per-item stored aisle → user name rule → parser default.

Two entry points for a rule:

  • Inline hook on the item detail sheet: changing an aisle reveals an "Always file <name> in <aisle>" checkbox. Toggling on writes the rule (and keeps it synced if the aisle changes); off removes it.
  • Manager: Settings → Grocery → Category rules — a create row (item name + aisle picker) and a list of "name → aisle" rules with delete-and-confirm.

Rules live in a new local GroceryCategoryOverride table that mirrors the GroceryAutocompleteItem sync scaffolding (remoteId/clientId/isDirty/ownerId), so wiring the imminent Supabase grocery sync is a data-only follow-up — no schema migration. categoryKey is stored as a free-form string, so Phase 2 custom aisles slot in without an override-schema change. Sign-in/sign-out clear the rules alongside the other local repositories.

Commits (one concern each, per repo convention)

  1. fix(grocery) — categorize cold brew as a beverage (the original itch)
  2. feat(grocery) — override table + repository + resolution in GroceryRepositoryImpl
  3. feat(grocery) — "always file here" hook on item detail
  4. feat(grocery) — category rules management screen (new grocery/category-rules module + settings nav wiring)
  5. test(grocery) — previews + recorded screenshots
  6. test(grocery) — robot UI navigation test

Testing

  • Data layer: GroceryCategoryOverrideRepositoryImplTest (upsert, case-insensitive dedup, unrecognized-key drop) + 4 resolution tests in GroceryRepositoryImplTest (rule beats parser default; per-item aisle beats rule; removal reverts; case-insensitive parsed-name match).
  • BLoC: detail hook cases in GroceryDetailBlocTest; GroceryCategoryRulesViewModelTest; settings-root + app-settings navigation tests.
  • Screenshots re-recorded (category rules light/dark/empty/creating; detail "always file here" checked; app settings + settings root gain the new row).
  • Robot flow test opens Settings → Category rules and asserts the screen loads — also confirms the DI graph resolves the new repository across the app.

Reviewer notes

  • New feature module client/grocery/category-rules/{public,impl,impl-robots} mirrors the existing grocery/autocomplete module.
  • GroceryRepositoryImpl now depends on GroceryCategoryOverrideRepository; the test DI graph (BaseTestApplicationComponent) provides the new GroceryCategoryOverrideQueries accordingly.
  • Phase 2 (deferred): user-created custom aisles — outlined in the plan; requires replacing bare GroceryCategory usage with a merged aisle model across ~15 files.

🤖 Generated with Claude Code

plusmobileapps and others added 6 commits July 19, 2026 23:24
"Cold brew" wasn't in the IngredientParser vocabulary, so it fell through
to OTHER. Add it as a BEVERAGES keyword; sorted longest-first, it also
takes precedence over the generic "coffee" keyword.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds persistent "always file <name> under <aisle>" rules so users can
retag items the built-in IngredientParser sends to the wrong aisle (or to
OTHER). A new GroceryCategoryOverride table + GroceryCategoryOverrideRepository
back the rules, mirroring the GroceryAutocompleteItem sync scaffolding so a
later Supabase sync is data-only; the feature is local-only for now.

GroceryRepositoryImpl resolves each item's aisle as: per-item stored aisle
→ user name rule → parser default, combining the rule map into the grocery
list flows so rule changes re-emit. Sign-in/sign-out clear the rules
alongside the other local repositories.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a checkbox under the item detail aisle picker: "Always file <name> in
<aisle>". Toggling it on writes a persistent name→aisle rule (and keeps it
synced if the aisle is then changed); toggling off removes it. The box
initializes checked when a rule for this item's name already points at the
shown aisle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a Settings → Grocery → Category rules screen for reviewing and removing
name→aisle rules (created here or via the item detail "always file here"
hook). New grocery/category-rules feature module (bloc/impl/viewmodel/screen)
mirrors the grocery autocomplete settings module; the create row pairs an item
name with an aisle picker, and each rule row offers delete-with-confirm.

Wires the screen into settings navigation (SettingsRoot child + app settings
row) and registers the impl module in the DI graph.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds Compose previews and screenshot-test coverage for the new category
rules screen (populated, empty, creating, dark) and the item detail
"always file here" checked state. Re-records the grocery detail, app
settings, and settings root references to include the new checkbox row and
the "Category rules" settings entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the grocery/category-rules impl-robots module and a runRootBlocTest flow
that opens Settings → Category rules and asserts the management screen loads.
Registers the new GroceryCategoryOverrideQueries in the test DI graph, which
the grocery repository now depends on.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant