#109 — Checkbox-Semantik in der Einkaufsliste umkehren (leer = zu kaufen, angehakt = erledigt) #112

Closed
opened 2026-08-18 13:14:10 +02:00 by lena · 1 comment
lena commented 2026-08-18 13:14:10 +02:00 (Migrated from git.butzei.de)

Story: Checkbox-Semantik in der Einkaufsliste umkehren (leer = zu kaufen, angehakt = erledigt)

As a Nutzer der Einkaufsliste,
I want to dass ein neu hinzugefügtes Produkt mit einem leeren Kästchen erscheint, das ich abhake, wenn
ich es gekauft habe,
so that sich das Verhalten intuitiv anfühlt (leer = noch zu erledigen, angehakt = erledigt) statt
umgekehrt.

Wichtig — das ist eine Umkehr der bestehenden, bewusst getroffenen #90-Design-Entscheidung, kein reiner
Default-Wert-Fix:
ShoppingProductItem.tsxs eigener Kommentar dokumentiert die aktuelle Absicht explizit:
"every product rendered here is already active ... so it always starts checked and unchecking is how an
item gets taken off the list
". Das Kästchen bedeutet heute "ist aktiv auf der Liste" (angehakt) vs. "wurde
abgehakt und verschwindet von der Liste" (leer). Nur den Startwert auf "leer" umzustellen, ohne die
Bedeutung von an/abgehakt zu vertauschen, würde ein neues Produkt als "bereits erledigt" erscheinen lassen —
das Gegenteil vom gewünschten Verhalten. Diese Story kehrt die gesamte Bedeutung um, nicht nur den
Anfangswert.

Acceptance criteria:

  • Ein neu zur Liste hinzugefügtes/aktiviertes Produkt erscheint mit einem leeren Kästchen.
  • Ein Klick/Tap auf das Kästchen eines noch offenen Produkts hakt es ab und entfernt es von der
    aktiven Liste
    (bisher: Entfernen passierte beim Ab-haken eines bereits angehakten Kästchens —
    jetzt passiert dasselbe beim An-haken eines leeren Kästchens).
  • Alle Stellen, die sich auf die bisherige Bedeutung verlassen (z. B. onUncheck-Prop-Name/-Semantik in
    ShoppingProductItem.tsx, ggf. das "Recently checked off"-Restore-Verhalten aus #81, das nach eigener
    Beschreibung "checked off" = "von der Liste entfernt" meint) werden konsistent auf die neue Bedeutung
    umgestellt, nicht nur die Checkbox-Optik.
  • "Recently checked off" (#81) funktioniert unverändert als Wiederherstellungs-Funktion für gerade
    entfernte Produkte — nur der Auslöser (jetzt: ankreuzen statt abkreuzen) ändert sich.
  • Betrifft ausschließlich die Haupt-Produktansicht der Einkaufsliste. Der Online-Warenkorb
    (ShoppingCartCheckPanel, IsInCart) ist ein unabhängiges Konzept ("schon online bestellt") und von
    dieser Story nicht betroffen.

Out of scope for this story:

  • Vorratsschrank (Pantry) — hat ein eigenes, andersartiges Konzept (Bestandsmengen, kein einfaches
    An-/Abhaken) und ist von dieser Story nicht betroffen.
  • Todo-Listen — deren Checkbox-Semantik (offen/erledigt) ist bereits das gewünschte Verhalten und bleibt
    unverändert.
# Story: Checkbox-Semantik in der Einkaufsliste umkehren (leer = zu kaufen, angehakt = erledigt) **As a** Nutzer der Einkaufsliste, **I want to** dass ein neu hinzugefügtes Produkt mit einem leeren Kästchen erscheint, das ich abhake, wenn ich es gekauft habe, **so that** sich das Verhalten intuitiv anfühlt (leer = noch zu erledigen, angehakt = erledigt) statt umgekehrt. **Wichtig — das ist eine Umkehr der bestehenden, bewusst getroffenen `#90`-Design-Entscheidung, kein reiner Default-Wert-Fix:** `ShoppingProductItem.tsx`s eigener Kommentar dokumentiert die aktuelle Absicht explizit: "every product rendered here is already active ... so it always starts checked and **unchecking is how an item gets taken off the list**". Das Kästchen bedeutet heute "ist aktiv auf der Liste" (angehakt) vs. "wurde abgehakt und verschwindet von der Liste" (leer). Nur den Startwert auf "leer" umzustellen, ohne die Bedeutung von an/abgehakt zu vertauschen, würde ein neues Produkt als "bereits erledigt" erscheinen lassen — das Gegenteil vom gewünschten Verhalten. Diese Story kehrt die **gesamte Bedeutung** um, nicht nur den Anfangswert. **Acceptance criteria:** - [ ] Ein neu zur Liste hinzugefügtes/aktiviertes Produkt erscheint mit einem **leeren** Kästchen. - [ ] Ein Klick/Tap auf das Kästchen eines noch offenen Produkts hakt es ab **und entfernt es von der aktiven Liste** (bisher: Entfernen passierte beim *Ab*-haken eines bereits angehakten Kästchens — jetzt passiert dasselbe beim *An*-haken eines leeren Kästchens). - [ ] Alle Stellen, die sich auf die bisherige Bedeutung verlassen (z. B. `onUncheck`-Prop-Name/-Semantik in `ShoppingProductItem.tsx`, ggf. das "Recently checked off"-Restore-Verhalten aus `#81`, das nach eigener Beschreibung "checked off" = "von der Liste entfernt" meint) werden konsistent auf die neue Bedeutung umgestellt, nicht nur die Checkbox-Optik. - [ ] "Recently checked off" (`#81`) funktioniert unverändert als Wiederherstellungs-Funktion für gerade entfernte Produkte — nur der Auslöser (jetzt: ankreuzen statt abkreuzen) ändert sich. - [ ] Betrifft ausschließlich die Haupt-Produktansicht der Einkaufsliste. Der Online-Warenkorb (`ShoppingCartCheckPanel`, `IsInCart`) ist ein unabhängiges Konzept ("schon online bestellt") und von dieser Story nicht betroffen. **Out of scope for this story:** - Vorratsschrank (Pantry) — hat ein eigenes, andersartiges Konzept (Bestandsmengen, kein einfaches An-/Abhaken) und ist von dieser Story nicht betroffen. - Todo-Listen — deren Checkbox-Semantik (offen/erledigt) ist bereits das gewünschte Verhalten und bleibt unverändert.
lena commented 2026-08-18 13:14:10 +02:00 (Migrated from git.butzei.de)

design (109_shopping_checkbox_semantics_inversion_design.md)

Design note — #109 Checkbox semantics inversion (Shopping List)

Scope: Frontend only. No backend/API change — the underlying command that removes a product from
the active list is already DeactivateShoppingProductCommand (from #81/#90); this story only inverts
which checkbox gesture and starting state trigger it.

Change:

  • ShoppingProductItem.tsx: checkbox now renders checked={false} (was always checked). The
    onUncheck prop is renamed to onCheck — same callback signature and same underlying effect
    (deactivate + push to "recently checked off" history), just fired by the opposite gesture (checking
    an empty box, not unchecking a filled one).
  • ShoppingListPage.tsx: handleUncheck renamed to handleCheck, body unchanged (still calls
    deactivateShoppingProduct and pushShoppingCheckedOff).
  • RecentlyCheckedOffPanel.tsx / store.ts's shoppingCheckedOffHistory: no change needed — both are
    already gesture-agnostic ("history of recently-removed products"), not tied to the old
    checked/unchecked meaning.
  • Explicitly out of scope, confirmed unaffected: ShoppingCartCheckPanel/IsInCart (independent "already
    ordered online" concept) and Pantry (different domain, no plain check/uncheck concept).

Why no new component/prop needed beyond the rename: the story is a pure inversion of an existing
gesture, not a new capability — same command, same history mechanism, only the checkbox's default
visual state and which click direction fires the callback change.

Tests: ShoppingProductItem.test.tsx and ShoppingListPage.test.tsx updated to assert
not.toBeChecked() by default and to reference onCheck; the click-to-deactivate assertion itself
was already gesture-agnostic (fireEvent.click on the checkbox) and needed no behavioral change.

**design** (`109_shopping_checkbox_semantics_inversion_design.md`) # Design note — `#109` Checkbox semantics inversion (Shopping List) **Scope:** Frontend only. No backend/API change — the underlying command that removes a product from the active list is already `DeactivateShoppingProductCommand` (from `#81`/`#90`); this story only inverts which checkbox gesture and starting state trigger it. **Change:** - `ShoppingProductItem.tsx`: checkbox now renders `checked={false}` (was always `checked`). The `onUncheck` prop is renamed to `onCheck` — same callback signature and same underlying effect (deactivate + push to "recently checked off" history), just fired by the opposite gesture (checking an empty box, not unchecking a filled one). - `ShoppingListPage.tsx`: `handleUncheck` renamed to `handleCheck`, body unchanged (still calls `deactivateShoppingProduct` and `pushShoppingCheckedOff`). - `RecentlyCheckedOffPanel.tsx` / `store.ts`'s `shoppingCheckedOffHistory`: no change needed — both are already gesture-agnostic ("history of recently-removed products"), not tied to the old checked/unchecked meaning. - Explicitly out of scope, confirmed unaffected: `ShoppingCartCheckPanel`/`IsInCart` (independent "already ordered online" concept) and Pantry (different domain, no plain check/uncheck concept). **Why no new component/prop needed beyond the rename:** the story is a pure inversion of an existing gesture, not a new capability — same command, same history mechanism, only the checkbox's default visual state and which click direction fires the callback change. **Tests:** `ShoppingProductItem.test.tsx` and `ShoppingListPage.test.tsx` updated to assert `not.toBeChecked()` by default and to reference `onCheck`; the click-to-deactivate assertion itself was already gesture-agnostic (`fireEvent.click` on the checkbox) and needed no behavioral change.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
robert/todo#112
No description provided.