Hosting product picker and stale flag

Task 5653 · Done · Coder · created · updated

## Original requirement (verbatim, epic) Move all product related configuration from Settings to Products section in the Operator. Operator's ask, verbatim: "Create a task to merge these two concepts. User should be able to manage the list of supported instances - create UI with support of family, lookup from actual aws, showing params (cpu, ram, other) and price on-demand and current spot. user adds which we want to allow. on the hosting products section - when adding products only allow selecting from available, prefill the name from instance type, also allow easy navigation, by family, etc. show exclamation mark if the product has an instance type that is no longer allowed." Itemized requirements (verbatim): - 4. "**Hosting products pick only from allowed types.** Adding a Dedicated size offers only allowed types, with the same family navigation. The product name is prefilled from the instance type (e.g. "m6i.2xlarge · 8 vCPU · 32 GB") and stays editable." - 5. "**Flag stale products.** A product whose instance type is no longer allowed shows a warning icon with a one-line reason. It must never be orderable: the catalog marks it unavailable, so no tenant is charged for a size that cannot launch." Epic Done-means (verbatim): "A UI change: screenshots of both screens at desktop and 480px in the report." Full PRD: Task 5632; full design: Task 5633 (read with `task_read` if you need more than this body). ## This child (C7): Products › Hosting products — allowed-only picker, prefilled name, stale flag, evidence Builds on: C6's `InstanceTypeBrowser` (`apps/web/src/components/instanceTypes/InstanceTypeBrowser.tsx`, `pick` mode) + `instanceProductName`; C4's `allowed: bool | null` on `GET /api/billing/rental-prices` and the server create guard; C1's `GET /api/instance-types`; C5's moved `HostingPricesSection`/`HostingPriceDialog` under `apps/web/src/pages/products/`. All merged before you start. ### Settled decision (BA D9, verbatim) "a new Dedicated product's instance type must be an allowed type, enforced server-side on create (not only by the picker). Existing products' keys stay immutable (current rule, `HostingPriceDialog.tsx:12`). vCPU/RAM on the product are prefilled from the allow-list row." ### Approach (Architect — realize, do not reopen) - The add-instance dialog uses `InstanceTypeBrowser` in `pick` mode over `GET /api/instance-types` MINUS keys already in `rental_prices` (kind=instance) — so a save can never hit the duplicate-key 409. The free-text instance-type key field is REMOVED for instances (storage/shared dialogs unchanged). - Choosing a type prefills Name via `instanceProductName` (AD22: `m6i.2xlarge · 8 vCPU · 32 GB`; unknown specs → type alone) and vCPU/RAM fields; all stay editable and a user edit is kept (not overwritten on re-render). - **Stale flag (quality bar, §3):** an instance row with `allowed:false` shows a warning (exclamation) icon via MUI `Tooltip` on a FOCUSABLE `IconButton`/span with `aria-label` = the reason — never a bare `title` tooltip — reason text: "m6i.2xlarge is no longer an allowed instance type — tenants can't order it." Icon disappears when re-allowed. - **Stale count:** a count chip on the Hosting products tab label ("Hosting products · 2 stale") or a banner on the section. - Empty allow-list: the add-instance action explains no types are allowed and links to Products › Instance types. - Copy follows `docs/standards/ui-copy-standards.md`. ### Acceptance criteria (verbatim from PRD) - **AC6.1** "Add instance type" on Products › Hosting products offers only allowed types not already a product, with R4's family/arch/vCPU/RAM navigation and search; there is no free-text instance-type field. - **AC6.2** Choosing a type prefills Name as `<type> · <vCPU> vCPU · <RAM> GB` (e.g. "m6i.2xlarge · 8 vCPU · 32 GB") and vCPU/RAM fields; Name stays editable and an edit is kept. Unknown specs → Name prefilled with the type alone. - **AC6.4** Storage sizes and Shared tiers behave exactly as today. (co-owner: your dialog change must not alter them) - **AC7.1** In Products › Hosting products, an instance product whose type is not in the allow-list shows a warning (exclamation) icon with a one-line reason, e.g. "m6i.2xlarge is no longer an allowed instance type — tenants can't order it." visible on hover/focus and to screen readers; the icon disappears once the type is allowed again. - **AC7.3** The operator can see at a glance how many products are stale (e.g. count on the Hosting products tab or a banner). - **AC9.1** The report carries screenshots of Products › Instance types (allowed list + browse) and Products › Hosting products (add dialog with prefilled name, and a stale product's warning) at desktop (1440px) and 480px. - **Quality bar:** the picker is navigable on a long allowed list (same grouping/filters/search as the Instance types tab, no `<select>`), names are never truncated, the dialog is usable at 480px with no horizontal scroll; the stale reason is reachable by keyboard and screen reader. Credentials & reach: Component tests — None — **verified**. AC9.1 screenshots on dev (browser) — `gmind/skynet/dev` (dev operator UI login), dev control plane `dev.jaah.ai` — credential **verified**; environment **deferred**: dev resolves to private 10.60.x addresses unreachable from node h2; run from a node with dev reach, otherwise use the local-stack fallback below and say so. Out of scope: Do NOT change Shared tiers or storage sizes behaviour, pricing or data. Do NOT change existing products' keys (immutable). Do NOT change the server guards or API contracts (C4/C1/C2) — consume them. Do NOT re-implement the browser component (reuse C6's; extend it only if `pick` mode lacks something, keeping its other modes green). Do NOT auto-set product prices from AWS prices. Do NOT touch the tenant `HostingPicker` (C4). Epic: Shared tiers and storage sizes (unchanged), refunding the failed order, GCP/Azure. Done means: - AC6.1 — component test: allow-list {m8i.large, m8i.2xlarge, t4g.small}, product exists for t4g.small → picker offers exactly m8i.large, m8i.2xlarge; no free-text instance-type input rendered; family filter/search present. - AC6.2 — component test asserting the exact prefilled string "m6i.2xlarge · 8 vCPU · 32 GB" and vCPU/RAM, then editing Name and saving sends the edited name; unknown-spec type → Name = type alone. - AC6.4 — existing `HostingPricesSection.test.tsx` storage+shared cases stay green (import-path edits only). - AC7.1 — component test with a product on a disallowed type (`allowed:false`) → icon + accessible reason (`aria-label`/role query) present and focusable; same product with `allowed:true` → icon absent. - AC7.3 — component test: 2 stale products → the count ("2 stale") is visible on the tab or banner; 0 → no count. - AC9.1 — screenshots at 1440px and 480px of: Instance types allowed list, Instance types browse, Hosting products add dialog with prefilled name, Hosting products stale warning — in the report, from dev (deferred — dev.jaah.ai unreachable from node h2). Fallback when dev is unreachable: Playwright against the local `./dc` stack with seeded allowed types, a stale product and a stubbed browse response; state in the report which environment the shots came from. - `./dc check quick` green; Jaah Deploy for the merge succeeded.

Outcome

Add instance type picks only allowed, unused types, with the name prefilled; stale products are flagged and counted. PR #4971 merged; full gate, CI and Jaah Deploy green. Shots taken on the local stack (dev unreachable). Task creation refused (403): UI follow-ups are in the report.

Pull request

Artifacts

Activity

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