Products tab and Nodes link

Task 6072 · Done · Coder · created · updated

Epic ask (verbatim): "file an epic to create Customers section where operator can see all customers and fully manage their parameters (move all from Directory/Org) and fully manage all of their billing and products. Operator should be able to add/change/remove any products (without or with billing), see all history, etc. UI must be compliant with best practices and Jaah Standard (feed-like, tabbed pages, no prose on page, etc.)" Settled inputs: PRD = Task 6049, Architecture = Task 6050 (`task_read` both; §3.4 products API (built by C6a + C6b) and §3.8 Nodes are your contracts). This is child **C7**. Replace C3's `pages/customers/ProductsTab.tsx` slot body (`export function ProductsTab({ org }: { org: CustomerOrg })`) over the `/api/customers/{org_id}/products*` API. You run after C1 (history recording) is merged and deployed, so the live deletes you perform on dev ARE recorded in History (no backfill exists — D4); C8 verifies them. Requirement (verbatim, Req 4): "Products: the operator can add, change and remove ANY product for a customer (hostings/machines, plan features, add-ons, etc.). Each action can be done with billing (charged/prorated/refunded through Stripe) or without it (comped/manual, clearly marked as such)." Requirement (verbatim, Req 6): "The Nodes → Delete \"This machine is hosted. Delete it from the Billing page.\" dead end for operators routes into this customer's product management instead." Decision D8 (binding): a billed product is removed only with money (Credit unused days = default, or Refund to card); "Without billing" remove is offered only for Comped/Included products. D11: billed add has "Provision now" ON by default. Acceptance (verbatim from PRD, AC4.6 reshaped by D8): - AC4.1 Each product row shows: name, size/details, state, how it is paid — **Billed** (price/period from Stripe), **Included** (from the plan), or **Comped** (operator, with its end) — and, for a comp, the end date or "Never". Oracle: Wealthcharts' Products tab on dev lists its hosted machine with these fields. - AC4.2 (UI) Until paid a not-yet-provisioned billed add shows "Awaiting payment" with the invoice link. - AC4.3 (UI) a comped add reads "Comped · ends <date>" (or Never). - AC4.4 Change: resize (billed → quote/confirm; comped → comp line), change a comp's end, Comped → Billed, Billed → Comped, toggle feature switches. - AC4.5 Remove with billing: the dialog defaults to "Credit unused days on next invoice" and offers "Refund unused days to card now". Stripe computes both (removal-quote shows Stripe's figures). - AC4.7 Every add/change/remove dialog states the mode (With billing / Without billing) explicitly, and the destructive confirm names the customer, the product and the money outcome. No mode is preselected silently for a money-moving remove. Oracle: screenshot of the remove dialog at 390px — readable, every option tappable. - AC6.1 For an operator, deleting a hosted machine on Nodes/Hosts no longer dead-ends: one tap lands on `/customers/<wealthcharts>?tab=products` with the hosting highlighted. The tenant's message stays as today. - AC6.2 Operators see a hosted machine's size/customer on Nodes/Hosts ("<customer> · <size>"; today skipped at `Nodes.tsx:741`). - AC7.1/7.2 feed-like rows, no help prose, usable at 1440 and 390, dialogs full-height sheets at 390, tap targets ≥44px. - AC8.1 `by-node` route operator + `finance:view`; tenant 403/404. HOW (pinned): `ProductsTab.tsx` = feed of `GET .../products` items (Billed/Included/Comped chip, ends, invoice link, actions from `actions[]`); Add dialog (catalog from `GET .../catalog`, mode With billing → provision now/on payment; Without billing → end never/date/plan); Change dialogs for each PATCH op; Remove dialog per D8 with `removal-quote` figures; honour `?product=` by scrolling to and highlighting the row. Nodes (§3.8): `routes/nodes.py` `delete_node` — for an operator caller the 409 `detail` becomes `{"message": "This machine is hosted.", "customer": {"id","name"}, "rental_id", "href": "/customers/<org>?tab=products&product=<rental>"}`; tenant keeps `HOSTED_MACHINE` string unchanged. New `GET /api/customers/hostings/by-node?ids=` → `{"<node_id>": {"org":{"id","name"},"rental_id","size"}}` from `hostings.for_machines`; `Nodes.tsx` uses it for operators. Credentials & reach: - Operator browser oracles + PRD §5 step 1 on Wealthcharts: `jaah/debug/main/operator-login` on `https://app.dev.jaah.ai` — verified. - Stripe test confirmation of the billed delete: `jaah/debug/stripe-dev` — verified. Do NOT use `jaah/system/stripe-jaah-dev-sk`. - API tests, screenshots, `./dc check full`: worktree only — verified. Out of scope: Do NOT change the products API semantics built by C6a/C6b (only consume it; a bug in it may be fixed minimally and named); tenant Nodes message (`HOSTED_MACHINE`) and tenant Billing pages unchanged; do NOT build the History tab (C8); no new pricing models or Stripe Products/Prices; no money arithmetic in web or API (show Stripe's figures only); the operator never enters card numbers; must not require act-as (Task 6047); no new permission keys for tenant roles. Done means: - PRD §5 step 1 on dev after Jaah Deploy: as operator open Customers → Wealthcharts → Products; delete its hosted machine **with billing** (credit or refund) and then a comped one (add a comped Shared hosting first if needed) **without billing**. Report: the Stripe effect observed via `jaah/debug/stripe-dev` (object ids only) for the billed delete, that nothing changed in Stripe for the comped one, and that `GET /api/customers/<wealthcharts>/history` shows both with actor and mode. - AC4.1/4.2/4.3 — screenshot of Wealthcharts' Products tab showing Billed/Comped rows with paid-by, state, ends. - AC4.4 — on a dev test customer perform resize (comp), change comp end, Comped→Billed, Billed→Comped, toggle one switch; each succeeds and the feed updates after reload. - AC4.7 — Playwright screenshot of the remove dialog at 390 (readable, every option ≥44px, names customer + product + money outcome, no preselected money mode). - AC6.1 — on dev: Nodes → Delete on a hosted machine as operator → one tap lands on `/customers/<org>?tab=products&product=<rental>` with the row highlighted; pytest: tenant still gets the `HOSTED_MACHINE` string. - AC6.2 — operator Hosts tab shows "<customer> · <size>" for hosted machines (screenshot); pytest: `by-node` as tenant Owner → 403/404. - AC7.1/7.2 — Playwright screenshots of the Products tab and each dialog at 1440 and 390 attached to the PR, each judged usable (product names readable, no horizontal scroll, zero help paragraphs). - `./dc check full` green; the PR's Jaah Deploy to dev succeeds.

Outcome

Products tab (feed, Add/Resize/End/convert/Remove, D8 money) + Nodes 409 link + by-node sizes. PR #5284 merged, Jaah Deploy OK, all dev oracles passed incl. Stripe. Gate green but 3 smoke specs failing on develop too. 6 UX follow-ups in report.

Pull request

Artifacts

Activity

  • Draft
  • Ready
  • Runner started
  • Runner ended (done)