Product picker

The one way to attach catalog products (deal, dialer, meeting, order — D2), with free-text items that do not create a product (P7). App component ProductPicker (apps/web/src/components/ProductPicker.tsx): the same composition as Entity picker over the canonical Record select — products as <EntityRef type="product" removable> tokens, "Přidat" opens RecordSelect in adding mode.

Status: beta (AD 3 Oct 2026). The former duplicate shell (AddRefMenu) is gone (point C, 1 Oct 2026): panel, rows, states, keyboard and focus are Record select's. Canon after the DoD — stories, baseline, audit.

When to use

  • Products attached to a record: deal items, dialer, meeting, order lines.
  • A line that must not create a catalog product — free text (allowFreeText, default on).
  • One product (multi={false}): a pick replaces the previous token and closes the panel.

When not to use

  • People, companies, deals — Entity picker.
  • A rail row — Rail picker.
  • Quantities and prices — the line-item editor; this only picks the product.

Anatomy

  • Optional .acrm-field > .acrm-field-label (with label), then .eref-field: tokens via EntityRefPreview (catalog product opens /produkty/:id; free text is a plain token) + .eref-add > button.add-ref.
  • Panel: RecordSelect with anchorRef, keepOpen={multi}, filter={false}, onQueryChange (debounce 180 ms), minWidth={340}.
  • Empty query = top products by usage (GET /products/top, P6); from 2 characters fulltext (GET /products/search).
  • Row: product mark (entityType: 'product'), name, sub = category · status for draft / in-review products (admin + manager, P8). Already-added products are filtered out.
  • Free text: Record select footer row "Použít text „…“" (onCreate, icon writing), offered only when the query matches no found or already-added product by name.
  • States: loading only without data in hand; error "Produkty se nepodařilo načíst." with retry; empty catalog "Katalog zatím nemá žádné produkty".

Best practices

Do

  • Keep top products as the empty-query state — the fast path for repeat picks.
  • Use multi={false} for a one-to-one relation instead of trimming tokens in the caller.

Don't

  • A separate product dropdown per screen — D2 made this the single picker.
  • Prices, stock or descriptions in rows — name, category, status only.
  • Offer free text when an existing product has the same name (duplicate line without catalog link).

Content

  • "Přidat" (addLabel); search placeholder "Hledat produkt…" (placeholder).
  • Free-text row "Použít text „…“".
  • Status in sub from PRODUCT_STATUS ("koncept", "ke schválení").
  • entity-picker — the same composition for records.
  • record-select — the panel (adding mode, server search, footer create row).
  • entity-ref (variant removable) — the tokens.
  • Stories: Components / Record reference field — Default · States · Composition (DS.RecordRefField, acrm !4502).

Source: docs/select/product-picker.usage.md · GitLab