Skip to content

[FEAT](Sim Planner) Purchases: submodule totals global budget UX #1861

Description

@BenBotros

🎯 Feature Overview (Why?)

PRD #1555: "Either the submodule totals OR the Global budget must be
filled." The backend enforces the XOR and the one-entry-per-category rule at
creation (422 codes, planner_purchase = 81 / planner_purchase_budget =
82), but the UI shows both submodules side by side with no exclusivity
affordance — the user discovers the rule only via a raw error.

Design ↔ implementation divergence. The PRD/design screenshot shows the
submodule totals table and the Global budget field side by side, as if
both can be filled together — but the backend is XOR, so that layout
actively misleads. Mechanically disabling one half whenever the other holds
data makes the screen read as broken, not as a designed either/or. Properly
expressing the XOR needs a reworked design / new UI frame for the
Purchases section, decided with product/design — not bolted onto the
existing side-by-side layout. That design rework is the gating decision for
this feature; the items below are the interaction requirements it must
satisfy plus a candidate approach, not a settled UI.

🧩 Solution

Blocked on a design rework (see Overview): the side-by-side layout and
the XOR backend diverge, so the section needs a new design frame before
this UX is built. Park the design decision for product/design per the
guardrails. The items below are the
requirements that frame must satisfy plus a candidate mechanical approach,
not a settled UI.

  • In the planner Purchases module (bespoke plannerPurchase config,
    planner-module-config/module-configs.ts): the two modes (per-category
    submodule totals vs global budget) must be a visible, explicit
    either/or
    — the reworked design decides the affordance (e.g. a segmented
    control / mode toggle) rather than leaving the user to infer the rule from
    greyed-out fields.
  • Candidate mechanical fallback if the design keeps the current layout: when
    a global-budget entry exists, disable the per-submodule table/form with an
    explanatory hint — and vice-versa. Deleting the blocking entry re-enables
    the other mode without reload (both submodules' data live in the same
    module fetch).
  • Map the backend 422 error codes to i18n messages (en+fr) surfaced inline
    on the form, not as a raw-JSON toast — covers the race where two tabs
    create a budget and a submodule row concurrently (the second gets the
    server 422 despite UI gating).
  • One-entry-per-category duplicate rule surfaced inline on the category
    select (disable already-used categories or map the 422).
  • Missing-factor display: entries carry no kg CO₂eq until the average-EF
    factor rows are uploaded (data concern, not code) — show an em-dash or
    explanatory tooltip instead of a misleading hard 0.
  • Guard: reference-kg column and % slider must never appear for Purchases
    (manual type — no snapshot rows).

📋 Implementation Plan

  • Design rework decided with product/design first — a Purchases UI
    that expresses the XOR as an explicit either/or (mode affordance);
    gates the items below
  • Mutual-exclusion state derived from the module's entries; disable the
    opposite mode's form/table with hint
  • Re-enable on deletion of the blocking entry (no reload)
  • Enumerate the backend 422 codes from modules_planner/purchase and
    map each to an i18n message (en+fr), rendered inline
  • Duplicate-category rule surfaced on the category select
  • Missing-factor kg display convention (— / tooltip) per clarification
  • Amount edited to 0 still counts as "filled" (blocks the other mode) —
    deleting is the way to switch modes; hint says so
  • Verify no reference columns/slider render for Purchases

⚙️ Clarifications Needed

  • Design rework (hard blocker): the side-by-side design and the XOR
    backend diverge — a new/reworked Purchases design that expresses the
    either/or (mode affordance) is needed before the UX is built. Owned by
    product/design; gates the items below.
  • Exact 422 error-code list (enumerate from
    backend/app/modules_planner/purchase/ in review).
  • Missing-factor kg: vs 0 + tooltip?
  • Switching modes: require explicit deletion (recommended, matches backend),
    or offer a "switch to global budget" action that deletes for you (needs
    confirm)?

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions