Repository navigation
Commit 065bba7
feat(console): marketplace facet filters and a Beta badge from the spec taxonomy (#31187)
domain:ui#1 · os-project-manager · session_01FEkQ5oQ9zXD1QMdHFaqv1J
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XDp4MDE5jLtwYunGcCVPt5v)_
Fixes #31011
Clause-②: yes (narrowing)
Metadata-protocol: none
Skills-docs: none — no file under `skills/**` teaches the
`marketplace.category` keys or the console's marketplace filters (`git
grep -i "marketplace\.category"` over `skills/` and `content/docs/`: 0
hits); `content/docs/deployment/marketplace-handbook.mdx` says the
console's facet chips and Beta badge arrive as each side adopts the
taxonomy and a declared value is not yet shown, which stays true until
the catalog serves the columns.
## 维护者速读
- 改了什么:控制台市场页的单排「分类」小标签换成三个筛选维度:类型(应用 / 模板 /
连接器,单选,带「全部」)、职能、行业(可多选)。同一维度内是「或」,维度之间是「且」,每个取值后面显示数量。卡片显示主职能和 Beta
徽标,详情页显示全部职能、行业、类型和 Beta。
- 为什么改:旧分类已在 spec 里退役(#30904 的分类方案裁决),控制台还在读
`category`,翻译表也是旧枚举的手抄副本。现在取值直接来自 `@objectstack/spec/marketplace`
的枚举,翻译键按枚举值生成,有测试保证每个值都有翻译,不再另存一份列表。
- 破坏性:`@object-ui/i18n` 删除 `marketplace.category.*` 这组翻译键(旧分类枚举的副本),所以按
Clause-② 声明为 narrowing,changeset 里有 BREAKING 段(FROM / TO / 修法)和 ADR-0087
的 `not-required (runtime-interface-only …)` 处置标记。版本级别仍是 patch。
- 风险与回滚:cloud 目录在 objectstack-ai/cloud#3661
上线前不带这些字段,这期间所有筛选都隐藏,页面就是不带筛选栏的卡片列表(旧的分类小标签也一并去掉)。筛选仍在客户端,只覆盖已加载的 100
条。回滚即 revert。
-
先看:`ui/packages/app-shell/src/console/marketplace/marketplaceTaxonomy.ts`(筛选语义与「无数据即隐藏」),`MarketplaceFacetFilters.tsx`,`ui/packages/i18n/src/__tests__/marketplace-taxonomy-labels-31011.test.ts`。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/console/marketplace/__tests__/MarketplacePage.facets-31011.test.tsx`,预期
`Tests 11 passed (11)`。
- 需要你做的:无,席位落地。
## What changes
- **Facet filters** (`MarketplacePage.tsx`, new
`MarketplaceFacetFilters.tsx`, new `marketplaceTaxonomy.ts`). The
category chip row is replaced by three facets read from
`@objectstack/spec/marketplace`: `solutionType` as a segment with All,
`businessFunctions` and `industries` as toggle rows. OR inside a facet,
AND across facets and with the search box. Each value shows the count of
loaded listings carrying it under the other facets' selection. The
values are the spec schemas' `.options`, in the spec's order; a value is
drawn when a loaded listing carries it (or it is selected), so the
buttons do not jump while filtering. A facet that no loaded listing
carries is not drawn: with no taxonomy on the wire there is no filter
row at all, and industries hide while no listing carries one. A listing
that declares no solution type is counted and filtered as `app`
(`DEFAULT_SOLUTION_TYPE`, the spec's reading of an absent key). A
taxonomy column that is not an array reads as no values: its facet hides
and the page still renders.
- **Badges.** A catalog card shows the primary business function (the
first the listing names) and a Beta badge when `maturity === 'beta'`.
The Details page (`MarketplacePackagePage.tsx`) shows every business
function in the listing's order, the industries, the solution type (the
declared one, or `app` when none is declared, as the catalog counts it),
and the Beta badge, each facet as a labelled group.
- **Raw-value fallback dropped.** The old `categoryLabel` fell back to
the category's raw spelling when no label existed. A facet value now has
to be a spec value to be drawn anywhere; a row carrying anything else
draws nothing for it.
- **Wire** (`marketplaceApi.ts`). `MarketplacePackageSummary` declares
`business_functions`, `industries`, `solution_type` and `maturity`,
typed by the spec's taxonomy types, and drops `category`.
`listMarketplacePackages` drops its `category` parameter, which had no
caller.
- **i18n** (`ui/packages/i18n/src/namespaces/marketplace/*.ts`, 10
hand-written catalogues plus `zh-Hant` regenerated by `gen:zh-hant`).
`marketplace.category.*` (the copy of the retired enum) is deleted.
Added: `marketplace.businessFunction.*` (11), `marketplace.industry.*`
(10), `marketplace.solutionType.*` (3), `marketplace.maturity.beta`, and
three facet headings `marketplace.facet.*`.
- **Call-site key gate** (`ui/scripts/check-i18n-call-site-keys.mjs`).
The `marketplace.category.` dynamic family is gone, so its
`DYNAMIC_KEY_FAMILIES` entry would read stale; three new heads would
read undeclared. The entry is replaced by
`marketplace.businessFunction.`, `marketplace.industry.` and
`marketplace.solutionType.`, each `enumerable: false` / `why:
'external-vocabulary'` (the vocabulary is a spec enum this reader cannot
open), and each reason names the parity test that pins the set. The gate
itself is unchanged. One comment in
`dead-pack-keys-retired-8754.test.ts` that named the retired head now
names a live one.
- **Changeset** `.changeset/31011-marketplace-taxonomy-facets.md`:
`@object-ui/i18n` (published, its catalogues ship in `dist`) and
`@object-ui/app-shell` patch, `Clause-②: yes (narrowing)`,
`Metadata-protocol: none`. Removing `marketplace.category.*` from a
published package's catalogues narrows what a consumer can read, so the
changeset carries a **BREAKING** paragraph (FROM the 15 removed keys; TO
no one-for-one replacement, the facet keys
`marketplace.businessFunction|industry|solutionType.*` and
`LEGACY_CATEGORY_MAPPING` in `@objectstack/spec/marketplace`; the
one-line fix) and exactly one ADR-0087 disposition, `not-required
(runtime-interface-only
ui/packages/i18n/src/namespaces/marketplace/index.ts#MarketplaceCatalogue)`,
whose spelling `scripts/check-adr-0087-registration.mjs` accepts ("1
declared-breaking changeset(s), each carrying an ADR-0087 disposition",
exit 0).
## Why
The maintainer's taxonomy ruling on #30904 (「关掉,Beta 并入分类方案」 and D1–D3)
retired the single `category` for three facets and a maturity flag, and
PR #30980 landed them in the spec. The console still built its chips
from `category`, and its labels copied the retired enum, so a value
cloud served outside that copy was shown as its raw spelling.
## Tests
- New `MarketplacePage.facets-31011.test.tsx` (11 cases). Every click
goes through the rendered buttons (`fireEvent`), `fetch` is stubbed by
path with the catalog body, and labels are the `en` pack's: spec-order
facets with counts; OR inside a facet; AND across facets with counts
against the other facets; the solution-type segment including the
default `app` and All; search plus facets; no taxonomy draws no filter
and no badge; industries and type hide while only business functions are
served; off-spec values (`crm`, `general`, `theme`) drawn nowhere; the
card's primary-function badge; Beta on `beta` only; a list column that
is not an array (`business_functions: 'finance'`, `industries: {…}`)
reads as no values and the catalog still renders.
- New `MarketplacePackagePage.taxonomy-31011.test.tsx` (4 cases): every
business function in order, industries, type and Beta; a stable listing
with no industries; a listing with no taxonomy draws the default
solution type `app` and nothing else; off-spec values and list columns
that are not arrays are drawn nowhere.
- New
`ui/packages/i18n/src/__tests__/marketplace-taxonomy-labels-31011.test.ts`
(57 cases): for each of the 11 marketplace catalogues and each of
`MarketplaceBusinessFunctionSchema` / `MarketplaceIndustrySchema` /
`MarketplaceSolutionTypeSchema`, every `.options` value has a non-empty
label and no other key is labelled; `maturity` labels `beta` alone; no
catalogue keeps `category`. The catalogue set is held equal to
`MARKETPLACE_LANGUAGE_CODES`.
- **Ablations** (`scripts/ablation-replace.mjs`, each anchor hit once,
each restore proven blob == HEAD and `git diff HEAD` empty; the tests
import the edited files as source, so no build sits on the path). See
Verification.
## Acceptance notes
- **Expected 5 is not in this PR** (seat ruling): server-side filtering
and the dead `?category=` filter in
`packages/cloud-connection/src/marketplace-proxy-plugin.ts` go to a
sibling card the seat files. Filtering here stays on the client over the
`limit: 100` page.
- **Wire shape is an assumption until objectstack-ai/cloud#3661 ships.**
The reader expects `business_functions` and `industries` as JSON arrays
of spec values (the spec's shape for the camelCase keys) and
`solution_type` / `maturity` as strings. Until the catalog serves them
every facet hides, as the card says.
- **Off-spec values.** The cloud catalog stores the facets as select
fields whose options are the spec's, so an off-spec value should not
reach the console; if one does it is drawn nowhere rather than as its
raw spelling. Not a tolerance shim: nothing is mapped or aliased.
- **Detail page solution type.** An undeclared type is drawn as `app`
(`DEFAULT_SOLUTION_TYPE`), the same reading the catalog's filter and
counts use, so the two pages agree.
- **Filter state is component state**, as the category filter and the
search box were; nothing moved to the URL.
- **Test fixtures still carrying `category`** in eight older marketplace
tests (most of them org-catalog rows, whose `OrgPackageSummary.category`
is untouched) were left as they are: they are untyped literals that
nothing reads.
- **Locale count.** The order named nine namespace locales; the tree has
eleven marketplace catalogues (ten hand-written plus the generated
`zh-Hant`), all updated.
- **`zh` Beta label.** `untranslated-identity-4376.test.ts` refuses an
English value verbatim in a non-Latin pack, so `zh-Hans` reads `测试版`
(`zh-Hant` `測試版`); the other non-Latin packs translate it too.
## Verification
All at head `2296ac23` (base `8ef4d309`; it merges cleanly onto
`origin/main` `66bf5b4a`). Heavy runs went through
`scripts/pm/os-verify-lock.sh`. Round 1 ran on `4f2029f9`; every run
below was repeated on `2296ac23`.
<details><summary>Package suites and type-check</summary>
- Prerequisite: `pnpm --workspace-concurrency=2 --filter
'@object-ui/app-shell^...' --filter '@object-ui/i18n^...' build`,
VERDICT command-exit 0.
- `pnpm --dir ui exec vitest run
packages/app-shell/src/console/marketplace/ packages/i18n/`: Test Files
156 passed (156), Tests 2313 passed | 13 skipped (2326). The first run
on `b3457e91` had one red, `untranslated-identity-4376.test.ts`
(`zh-Hans` / `zh-Hant` `marketplace.maturity.beta` was the English
`Beta`); `4f2029f9` translates it.
- `pnpm --filter @object-ui/i18n --filter @object-ui/app-shell run
type-check` (each runs `tsc --noEmit` and `tsc -p tsconfig.test.json`,
which includes the new tests): both Done, VERDICT command-exit 0.
- Round 1 also ran `pnpm --dir ui exec eslint` over the touched source
and test files: exit 0, 0 errors (55 warnings, all `no-explicit-any` in
the pre-existing page and API files).
</details>
<details><summary>Dogfood battery</summary>
- `pnpm --workspace-concurrency=2 --filter '@objectstack/dogfood^...'
build && pnpm --filter @objectstack/dogfood test` under the lock: Test
Files 435 passed | 3 skipped (438), Tests 3609 passed | 41 skipped
(3650), VERDICT command-exit 0. No failures.
</details>
<details><summary>Ablations (10, each red, each restored)</summary>
Each through `scripts/ablation-replace.mjs`; the anchor hit once, the
mutation was proven on disk by anchor count and blob change, and the
restore by blob == HEAD and `git diff HEAD` empty. The tests read the
edited files as source, so no build sits on the path. A1 to A8 ran on
`4f2029f9`; A9 and A10 cover this round's changes and ran on `2296ac23`.
| # | Mutation | Red |
|:--|:--|:--|
| A1 | OR inside business function turned into every-of (`.some` to
`.every`) | facets test 2 failed / 8 passed (OR, AND) |
| A2 | the industry selection ignored | 1 failed / 9 passed (AND) |
| A3 | Beta for any maturity but `stable` | 3 failed / 10 passed across
both page files (Beta only for beta, no-taxonomy cases) |
| A4 | every spec value drawn whether or not a listing carries it | 4
failed / 6 passed (spec-order facets, both hide cases, off-spec) |
| A5 | the off-spec filter on business functions removed | 2 failed / 11
passed (off-spec drawn nowhere, page and Details) |
| A6 | an undeclared solution type not read as `app` | 2 failed / 8
passed (counts, segment) |
| A7 | `en` `marketplace.businessFunction.legal` deleted (a spec value
with no label) | parity test 1 failed / 56 passed |
| A8 | `en` labels a value the spec does not list (`crm`) | parity test
1 failed / 56 passed |
| A9 | the `Array.isArray` guard in `readListingTaxonomy` removed (a
column trusted as a list) | 2 failed / 13 passed across both page files
(the non-array cases on the catalog and on Details) |
| A10 | Details draws only a declared solution type | detail test 2
failed / 2 passed (no taxonomy draws `app`; off-spec and non-array) |
A8's first attempt was refused by the tool before running (its
replacement contained the anchor); it was rerun with a distinct
replacement, and the reading above is the rerun.
</details>
<details><summary>Derived gates (dispatch-gates --commands, 38
families)</summary>
- 38 run, all exit 0. `dispatch-gates.mjs --ran`: "38 derived famil(ies)
accounted for — 38 run, 0 NOT-MEASURED".
- Two of them did not measure on the first pass and were rerun under the
lock. `pnpm --dir ui exec vitest run scripts/` was killed with SIGKILL
when memory ran out beside the dogfood build, and `pnpm
check:type-check-debt` exited 3 (the lock queue timed out, so nothing
was built). On the rerun, `vitest run scripts/` (which holds the
call-site key gate's own suite) gave Test Files 170 passed | 2 skipped
(172), Tests 4996 passed | 2 skipped (4998). `check:type-check-debt`
re-measured 0 ledger entries, with 0 raw tsc errors.
- Run beyond the derivation, all exit 0: `pnpm --dir ui run
check:i18n-keys`, `check:i18n-dead-keys`, `check:new-line-citations`.
- `node scripts/check-adr-0087-registration.mjs --base origin/main`: "1
declared-breaking changeset(s), each carrying an ADR-0087 disposition",
exit 0.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (Not touched.)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
<!-- signed: domain:ui#1 · os-project-manager ·
session_01FEkQ5oQ9zXD1QMdHFaqv1J -->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 86e9ef0 commit 065bba7
22 files changed
Lines changed: 1401 additions & 233 deletions
File tree
- .changeset
- ui
- packages
- app-shell/src/console/marketplace
- __tests__
- i18n/src
- __tests__
- namespaces/marketplace
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
| 118 | + | |
118 | 119 | | |
119 | 120 | | |
120 | 121 | | |
| |||
1461 | 1462 | | |
1462 | 1463 | | |
1463 | 1464 | | |
1464 | | - | |
1465 | | - | |
1466 | | - | |
| 1465 | + | |
| 1466 | + | |
| 1467 | + | |
| 1468 | + | |
| 1469 | + | |
| 1470 | + | |
| 1471 | + | |
| 1472 | + | |
| 1473 | + | |
| 1474 | + | |
| 1475 | + | |
| 1476 | + | |
| 1477 | + | |
| 1478 | + | |
| 1479 | + | |
| 1480 | + | |
| 1481 | + | |
| 1482 | + | |
| 1483 | + | |
| 1484 | + | |
| 1485 | + | |
| 1486 | + | |
| 1487 | + | |
1467 | 1488 | | |
1468 | 1489 | | |
1469 | 1490 | | |
| |||
1578 | 1599 | | |
1579 | 1600 | | |
1580 | 1601 | | |
1581 | | - | |
| 1602 | + | |
| 1603 | + | |
| 1604 | + | |
1582 | 1605 | | |
1583 | 1606 | | |
1584 | 1607 | | |
| |||
1594 | 1617 | | |
1595 | 1618 | | |
1596 | 1619 | | |
| 1620 | + | |
| 1621 | + | |
| 1622 | + | |
| 1623 | + | |
| 1624 | + | |
| 1625 | + | |
| 1626 | + | |
| 1627 | + | |
| 1628 | + | |
| 1629 | + | |
| 1630 | + | |
| 1631 | + | |
| 1632 | + | |
| 1633 | + | |
| 1634 | + | |
| 1635 | + | |
1597 | 1636 | | |
1598 | 1637 | | |
1599 | 1638 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
60 | 68 | | |
61 | 69 | | |
62 | 70 | | |
| |||
360 | 368 | | |
361 | 369 | | |
362 | 370 | | |
363 | | - | |
| 371 | + | |
364 | 372 | | |
365 | 373 | | |
366 | 374 | | |
| |||
549 | 557 | | |
550 | 558 | | |
551 | 559 | | |
552 | | - | |
553 | | - | |
554 | | - | |
555 | | - | |
556 | | - | |
557 | | - | |
558 | | - | |
559 | | - | |
560 | | - | |
561 | | - | |
562 | | - | |
563 | | - | |
564 | | - | |
565 | | - | |
566 | | - | |
567 | | - | |
568 | | - | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
569 | 564 | | |
570 | | - | |
571 | | - | |
572 | | - | |
573 | | - | |
574 | | - | |
575 | | - | |
576 | | - | |
577 | | - | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
578 | 578 | | |
579 | 579 | | |
580 | 580 | | |
| |||
692 | 692 | | |
693 | 693 | | |
694 | 694 | | |
695 | | - | |
| 695 | + | |
696 | 696 | | |
697 | 697 | | |
698 | 698 | | |
| |||
703 | 703 | | |
704 | 704 | | |
705 | 705 | | |
706 | | - | |
707 | | - | |
708 | | - | |
709 | | - | |
710 | | - | |
711 | | - | |
712 | | - | |
713 | | - | |
714 | | - | |
715 | | - | |
716 | | - | |
717 | | - | |
718 | | - | |
719 | | - | |
720 | | - | |
721 | | - | |
722 | | - | |
723 | | - | |
724 | | - | |
725 | | - | |
726 | | - | |
| 706 | + | |
727 | 707 | | |
728 | 708 | | |
729 | 709 | | |
| |||
900 | 880 | | |
901 | 881 | | |
902 | 882 | | |
903 | | - | |
| 883 | + | |
| 884 | + | |
904 | 885 | | |
905 | 886 | | |
906 | 887 | | |
| |||
962 | 943 | | |
963 | 944 | | |
964 | 945 | | |
965 | | - | |
966 | | - | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
967 | 957 | | |
968 | 958 | | |
969 | 959 | | |
| |||
0 commit comments