OSAC-2932: [UI] Catalog management list page with resource type tabs - #100
Conversation
…views Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
…ggle) Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
…ItemPublishToggle components Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
… CatalogItemCard Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
|
@ElayAharoni: This pull request references OSAC-2932 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
WalkthroughAdds a role-aware admin catalog management page with resource tabs, search and publication filters, private/public catalog hooks, scope and status labels, card metadata, empty states, and create/detail navigation. ChangesCatalog management
Estimated code review effort: 4 (Complex) | ~50 minutes Sequence Diagram(s)sequenceDiagram
participant Admin as AdminCatalogRoutes
participant Page as CatalogManagementListPage
participant Panel as CatalogManagementPanel
participant API as CatalogListHook
participant Card as CatalogItemCard
Admin->>Page: Render catalog index
Page->>Panel: Select resource tab and pass filters
Panel->>API: Fetch role-specific catalog items
API-->>Panel: Return catalog items
Panel->>Card: Render filtered items with scope and status labels
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 11✅ Passed checks (11 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@libs/ui-components/src/api/v1/baremetal-instance.test.ts`:
- Around line 40-89: Deduplicate the identical
createTestTransport/renderWithSession harnesses in baremetal-instance.test.ts
(lines 40-89), cluster-catalog-item.test.ts (lines 40-89), and
compute-instance-catalog-item.test.ts (lines 40-89) by moving the shared
provider wiring into TestProviders or adding a parameterized
renderHookWithProviders helper; retain per-test service and route configuration
through parameters, then replace all three local wrappers with the shared
harness.
In `@libs/ui-components/src/api/v1/baremetal-instance.ts`:
- Around line 36-44: Update useBareMetalInstanceCatalogItems to accept
parameters in the sibling-hook order, using (params: ListParams = {}, enabled =
true), while preserving its existing queryKey, queryFn, select, and enabled
behavior.
In `@libs/ui-components/src/api/v1/cluster-catalog-item.ts`:
- Around line 31-35: Update the doc comment for the admin list hook to state
that Tenant Admins access their tenant’s items through the public API, which
returns only published catalog items; remove the claim that unpublished items
are visible. Keep the CSP Admin/private API behavior unchanged.
- Around line 36-69: Extract the duplicated role-aware logic from
useAdminClusterCatalogItems and useAdminSetClusterCatalogItemPublished into
shared createAdminCatalogListHook and createAdminPublishMutationHook factories.
Parameterize the factories with the base query key and public/private clients,
preserving providerAdmin selection, enabled handling, private query-key suffix,
published update mask, and matching query invalidation behavior; update the
cluster hooks to use them.
In
`@libs/ui-components/src/components/catalogManagement/CatalogItemStatusLabel.test.tsx`:
- Around line 14-17: Update the test case for CatalogItemStatusLabel with
published={false} to assert that the closest pf-v6-c-label element has the
pf-m-grey class, replacing the current negative assertion against pf-m-green.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: osac-project/coderabbit/.coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 0d5c6dc9-adae-4eb1-abd0-a99d3b1aedaf
📒 Files selected for processing (27)
apps/app-frontend/src/shell/AdminCatalogRoutes.tsxlibs/i18n/locales/en/translation.jsonlibs/ui-components/src/api/types.tslibs/ui-components/src/api/v1/baremetal-instance.test.tslibs/ui-components/src/api/v1/baremetal-instance.tslibs/ui-components/src/api/v1/cluster-catalog-item.test.tslibs/ui-components/src/api/v1/cluster-catalog-item.tslibs/ui-components/src/api/v1/compute-instance-catalog-item.test.tslibs/ui-components/src/api/v1/compute-instance-catalog-item.tslibs/ui-components/src/components/catalog/CatalogItemCard.csslibs/ui-components/src/components/catalog/CatalogItemCard.test.tsxlibs/ui-components/src/components/catalog/CatalogItemCard.tsxlibs/ui-components/src/components/catalog/CatalogItemListSection.test.tsxlibs/ui-components/src/components/catalog/CatalogItemListSection.tsxlibs/ui-components/src/components/catalog/catalogItemDisplay.test.tslibs/ui-components/src/components/catalog/catalogItemDisplay.tslibs/ui-components/src/components/catalogManagement/CatalogItemPublishToggle.test.tsxlibs/ui-components/src/components/catalogManagement/CatalogItemPublishToggle.tsxlibs/ui-components/src/components/catalogManagement/CatalogItemScopeBadge.test.tsxlibs/ui-components/src/components/catalogManagement/CatalogItemScopeBadge.tsxlibs/ui-components/src/components/catalogManagement/CatalogItemStatusLabel.test.tsxlibs/ui-components/src/components/catalogManagement/CatalogItemStatusLabel.tsxlibs/ui-components/src/icons.test.tsxlibs/ui-components/src/icons.tsxlibs/ui-components/src/pages/admin/CatalogManagementListPage.test.tsxlibs/ui-components/src/pages/admin/CatalogManagementListPage.tsxlibs/ui-components/src/pages/admin/CatalogManagementTabPanel.tsx
…, doc accuracy Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
| @@ -0,0 +1,25 @@ | |||
| import CloudIcon from '@patternfly/react-icons/dist/esm/icons/cloud-icon'; | |||
There was a problem hiding this comment.
does this test bring any value ? We dont need test for each file
There was a problem hiding this comment.
Agreed — this only asserted a trivial 1:1 kind→icon lookup via exact SVG path comparison, low value. Removed the test file in 390f0cc.
| * the Tenant Admin's own tenant are not visible through this hook (tracked as a backend limitation in | ||
| * OSAC-3121). | ||
| */ | ||
| export const useAdminBareMetalInstanceCatalogItems = (params: ListParams = {}, enabled = true) => { |
There was a problem hiding this comment.
Lets not have private and public hooks in one file.
Lets update the structure to
api/v1- what we already have, for public APIsapi/v1/private- for private APIs
There was a problem hiding this comment.
Agreed and done — split into a plain public hook in api/v1/ and a plain private hook in the new api/v1/private/ for each of the three catalog item kinds (390f0cc).
| const isProviderAdmin = role === 'providerAdmin'; | ||
| const publicResult = useBareMetalInstanceCatalogItems(params, enabled && !isProviderAdmin); | ||
| const privateClient = useApiFetch(PrivateBareMetalInstanceCatalogItems); |
There was a problem hiding this comment.
this hooks should always work only with PrivateBareMetalInstanceCatalogItems
lets not check role here. The caller should be resposible for calling the right hook to get the expected resources back.
There was a problem hiding this comment.
Agreed — deleted useAdminBareMetalInstanceCatalogItems (and the equivalent for cluster/compute-instance). The new admin panel components call the plain public or private hook directly based on role instead (390f0cc).
| const { role } = useSession(); | ||
| const isProviderAdmin = role === 'providerAdmin'; | ||
| const publicClient = useApiFetch(BareMetalInstanceCatalogItems); | ||
| const privateClient = useApiFetch(PrivateBareMetalInstanceCatalogItems); |
There was a problem hiding this comment.
same here - lets not mix role + private/public
There was a problem hiding this comment.
applies for all other catalog item hooks
There was a problem hiding this comment.
Same fix applied here — see the reply on the sibling comment (390f0cc).
| | 'v1/compute_instance_catalog_items_private' | ||
| | 'v1/cluster_catalog_items_private' | ||
| | 'v1/baremetal_instance_catalog_items_private'; |
There was a problem hiding this comment.
maybe
| | 'v1/compute_instance_catalog_items_private' | |
| | 'v1/cluster_catalog_items_private' | |
| | 'v1/baremetal_instance_catalog_items_private'; | |
| | 'v1/private/compute_instance_catalog_items | |
| | 'v1/private/cluster_catalog_items' | |
| | 'v1/private/baremetal_instance_catalog_items'; |
to mirror our proposed api/v1 hooks structure
There was a problem hiding this comment.
Agreed and applied (fixed the missing closing quote from the suggested diff) — 390f0cc.
| export type CatalogItem = | ||
| | ClusterCatalogItem | ||
| | BareMetalInstanceCatalogItem | ||
| | ComputeInstanceCatalogItem; | ||
| | ComputeInstanceCatalogItem | ||
| | PrivateClusterCatalogItem | ||
| | PrivateBareMetalInstanceCatalogItem | ||
| | PrivateComputeInstanceCatalogItem; |
There was a problem hiding this comment.
This is getting a bit out of hand.
Lets split the components for specific ones - each type here is a separate component
each component loads the data, handles loading/error states (ListPageBody..) and renders the items.
There was a problem hiding this comment.
Agreed. Replaced the single generic CatalogManagementTabPanel with three concrete components (Cluster/ComputeInstance/BareMetalInstance), each calling its own hook and owning its own loading/error handling via ListPageBody. Kept the shared CatalogItem union and generic display helpers (search, scope, subtitle) since those are genuinely reused across all three panels and the tenant page (390f0cc).
| | { level: 'organization'; name?: string } | ||
| | { level: 'project'; name: string }; | ||
|
|
||
| const isPrivateCatalogItem = (item: CatalogItem): item is PrivateCatalogItem => 'tenant' in item; |
There was a problem hiding this comment.
this is quite fragile check. Each object has $typeName which you can use to get the exact type - much better & safer
There was a problem hiding this comment.
Good catch, fixed — now checks item.$typeName.startsWith('osac.private.') (390f0cc).
| return isProviderAdmin ? privateResult : publicResult; | ||
| }; | ||
|
|
||
| export const useAdminSetBareMetalInstanceCatalogItemPublished = () => { |
There was a problem hiding this comment.
we should have a single hook for updating BM. Not for every operation - ie usePatchBareMetalInstance that is below. The caller then defines what gets updated
There was a problem hiding this comment.
This hook is gone now — see the publish-toggle removal below, there's no longer a BM catalog-item mutation to consolidate.
| default: { | ||
| const exhaustiveCheck: never = scope; | ||
| return exhaustiveCheck; | ||
| } |
There was a problem hiding this comment.
and how would this exhaustiveCheck get rendered ? It would fail
a good default could be Label gray, text Unknown
There was a problem hiding this comment.
Agreed — kept the compile-time exhaustiveness check (still fails the build if a scope level is added without updating this switch) but the runtime fallback now renders a grey 'Unknown' Label instead of the never value (390f0cc).
| const setClusterPublished = useAdminSetClusterCatalogItemPublished(); | ||
| const setComputeInstancePublished = useAdminSetComputeInstanceCatalogItemPublished(); | ||
| const setBareMetalPublished = useAdminSetBareMetalInstanceCatalogItemPublished(); |
There was a problem hiding this comment.
these hooks are not used in this component, so why not call them in the child that actually uses them ?
There was a problem hiding this comment.
Agreed and fixed — CatalogManagementListPage no longer calls any data hooks, it only owns the tab/search/filter UI state. Each of the three new panel components calls its own hooks (390f0cc).
| onChange: (published: boolean) => void; | ||
| } | ||
|
|
||
| const CatalogItemPublishToggle = ({ |
There was a problem hiding this comment.
i think this toggle may be breaking accessibility. How about after user clicks on the catalogitem - we show the drawer and in the drawer actions they can publish/unpublish.
There was a problem hiding this comment.
Took this suggestion — removed the publish/unpublish toggle from this story's scope entirely (component, prop, CSS z-index workaround, and the mutation hooks all deleted). It moves to a detail-page action alongside Edit/Delete in OSAC-2933, which avoids the nested-interactive-control-in-a-clickable-card problem you're flagging. Updated both OSAC-2932 and OSAC-2933 Jira tickets to reflect the scope move (390f0cc).
…public/private hooks Removes the card-level publish/unpublish toggle from this story's scope per reviewer suggestion; it moves to a detail-page action in OSAC-2933 instead, avoiding a nested interactive control inside an already-clickable card. Splits each admin catalog-item hook into a plain public hook (api/v1/) and a plain private hook (api/v1/private/), with role-based selection now living in the consuming panel component rather than inside the hooks themselves. Replaces the single generic CatalogManagementTabPanel with three concrete per-kind panels that own their own data fetching and compose ListPageBody + Gallery + CatalogItemCard directly. Also fixes the fragile 'tenant' in item type guard to use $typeName, gives the scope badge's exhaustiveness fallback a safe rendered default instead of a type-only never-check, and drops the low-value per-icon-kind test file. Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
…agement-list-page # Conflicts: # libs/ui-components/src/api/v1/baremetal-instance.test.ts # libs/ui-components/src/api/v1/baremetal-instance.ts
Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
libs/ui-components/src/pages/admin/ClusterCatalogManagementPanel.tsx (1)
1-160: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftThree near-identical panel components — extract shared data/layout logic.
ClusterCatalogManagementPanel,ComputeInstanceCatalogManagementPanel, andBareMetalInstanceCatalogManagementPanelare ~95% copy-pasted (state derivation, search/publication filtering, control bar, empty state, gallery rendering) and differ only in the hook pair, title text, and route segment. This already shows early drift — theisSuccess-vs-isLoadingempty-state rationale is documented only in the Cluster panel's comment, not the other two. A shared internal hook (e.g.useCatalogManagementPanelData(publicHook, privateHook, role, isActive, search, publicationFilter)) plus a shared presentational component parameterized bytitle,routeBase, andemptyMessageswould keep the three files as thin, per-type wrappers (preserving the earlier requested split) while removing ~300 lines of duplicated logic and preventing future divergence.
libs/ui-components/src/pages/admin/ClusterCatalogManagementPanel.tsx#L1-L160: extract the shared data-fetch/filter/render logic into a common hook/helper and keep this file as a thin per-type wrapper.libs/ui-components/src/pages/admin/ComputeInstanceCatalogManagementPanel.tsx#L1-L163: adopt the same shared helper instead of re-duplicating the Cluster panel's logic.libs/ui-components/src/pages/admin/BareMetalInstanceCatalogManagementPanel.tsx#L1-L163: adopt the same shared helper instead of re-duplicating the Cluster panel's logic.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@libs/ui-components/src/pages/admin/ClusterCatalogManagementPanel.tsx` around lines 1 - 160, Three catalog management panels duplicate the same data, filtering, controls, empty state, and gallery rendering logic. In libs/ui-components/src/pages/admin/ClusterCatalogManagementPanel.tsx:1-160, extract shared hook/presentational logic and retain only the cluster-specific wrapper configuration. Update libs/ui-components/src/pages/admin/ComputeInstanceCatalogManagementPanel.tsx:1-163 and libs/ui-components/src/pages/admin/BareMetalInstanceCatalogManagementPanel.tsx:1-163 to use the shared implementation, parameterized by their hooks, title, route base, and empty-state messages, while preserving the isSuccess-based empty-state behavior.libs/ui-components/src/pages/admin/CatalogManagementListPage.tsx (1)
15-22: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse
isLoadingwhen gating admin catalog access by role.
useSession()is only safe insideSessionProvider, which is already mounted after the initial auth loading/error handling. The remaining race is updates torolefromuseOIDCLogin()while still loading; in that state this page falls back to the non-provider path. Add anisLoadingboundary around renderingAdminCatalogRoutes/CatalogManagementListPageso/admin/catalogonly renders when the selected role is final.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@libs/ui-components/src/pages/admin/CatalogManagementListPage.tsx` around lines 15 - 22, The admin catalog route currently renders before the role from useOIDCLogin() is finalized. Update the admin catalog access gate surrounding AdminCatalogRoutes and CatalogManagementListPage to include the session/auth isLoading state, rendering those components only after loading completes while preserving the existing role-based authorization behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@libs/ui-components/src/api/v1/baremetal-instance.test.ts`:
- Around line 21-55: Extract the repeated useBareMetalInstanceCatalogItems
fixture, fetch-success test, and disabled-fetch test into a shared parameterized
createCatalogHookTests factory. In
libs/ui-components/src/api/v1/baremetal-instance.test.ts:21-55, define and use
the factory; update
libs/ui-components/src/api/v1/cluster-catalog-item.test.ts:21-55 to pass
useClusterCatalogItems, ClusterCatalogItems, and tenantAdmin; update
libs/ui-components/src/api/v1/private/baremetal-instance-catalog-item.test.ts:22-56,
libs/ui-components/src/api/v1/private/cluster-catalog-item.test.ts:22-56, and
libs/ui-components/src/api/v1/private/compute-instance-catalog-item.test.ts:22-56
to invoke it with their respective private hook/service symbols and
providerAdmin. Preserve each test’s item fixture and existing assertions.
---
Outside diff comments:
In `@libs/ui-components/src/pages/admin/CatalogManagementListPage.tsx`:
- Around line 15-22: The admin catalog route currently renders before the role
from useOIDCLogin() is finalized. Update the admin catalog access gate
surrounding AdminCatalogRoutes and CatalogManagementListPage to include the
session/auth isLoading state, rendering those components only after loading
completes while preserving the existing role-based authorization behavior.
In `@libs/ui-components/src/pages/admin/ClusterCatalogManagementPanel.tsx`:
- Around line 1-160: Three catalog management panels duplicate the same data,
filtering, controls, empty state, and gallery rendering logic. In
libs/ui-components/src/pages/admin/ClusterCatalogManagementPanel.tsx:1-160,
extract shared hook/presentational logic and retain only the cluster-specific
wrapper configuration. Update
libs/ui-components/src/pages/admin/ComputeInstanceCatalogManagementPanel.tsx:1-163
and
libs/ui-components/src/pages/admin/BareMetalInstanceCatalogManagementPanel.tsx:1-163
to use the shared implementation, parameterized by their hooks, title, route
base, and empty-state messages, while preserving the isSuccess-based empty-state
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: osac-project/coderabbit/.coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 38fdf6e6-bbba-4726-b3ac-805c52363e84
📒 Files selected for processing (22)
libs/ui-components/src/api/types.tslibs/ui-components/src/api/v1/baremetal-instance.test.tslibs/ui-components/src/api/v1/baremetal-instance.tslibs/ui-components/src/api/v1/cluster-catalog-item.test.tslibs/ui-components/src/api/v1/compute-instance-catalog-item.test.tslibs/ui-components/src/api/v1/private/baremetal-instance-catalog-item.test.tslibs/ui-components/src/api/v1/private/baremetal-instance-catalog-item.tslibs/ui-components/src/api/v1/private/cluster-catalog-item.test.tslibs/ui-components/src/api/v1/private/cluster-catalog-item.tslibs/ui-components/src/api/v1/private/compute-instance-catalog-item.test.tslibs/ui-components/src/api/v1/private/compute-instance-catalog-item.tslibs/ui-components/src/components/catalog/CatalogItemCard.test.tsxlibs/ui-components/src/components/catalog/CatalogItemCard.tsxlibs/ui-components/src/components/catalog/catalogItemDisplay.tslibs/ui-components/src/components/catalogManagement/CatalogItemScopeBadge.tsxlibs/ui-components/src/pages/admin/BareMetalInstanceCatalogManagementPanel.tsxlibs/ui-components/src/pages/admin/CatalogManagementListPage.test.tsxlibs/ui-components/src/pages/admin/CatalogManagementListPage.tsxlibs/ui-components/src/pages/admin/ClusterCatalogManagementPanel.tsxlibs/ui-components/src/pages/admin/ComputeInstanceCatalogManagementPanel.tsxlibs/ui-components/src/pages/tenant/CatalogPage.tsxlibs/ui-components/src/test-utils/TestProviders.tsx
CodeRabbit flagged that the "fetches items" / "does not fetch when disabled" test pair was copy-pasted across all five catalog-item list hook test files (public and private, all three kinds), differing only in hook/service/role. Extracts createCatalogHookTests, which owns the two test bodies while each call site supplies its own concretely-typed registerList callback, so the mock service registration stays fully type-checked against the real Connect service descriptor. Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
…agement-list-page # Conflicts: # libs/ui-components/src/api/types.ts
…agement-list-page
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: batzionb, ElayAharoni The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
After reverting OSAC-2932 (osac-project#100), the "Catalog management" nav item still pointed at an empty placeholder page (the OSAC-2931 scaffold that osac-project#100 built on top of). Remove the nav item, the now-empty Administration nav section, the /admin/catalog route, and the AdminCatalogRoutes placeholder so nothing links to dead UI. Assisted-by: Claude Code <noreply@anthropic.com> Signed-off-by: Elay Aharoni <elayaha@gmail.com>
OSAC-2932: [UI] Catalog management list page with resource type tabs
Jira: https://redhat.atlassian.net/browse/OSAC-2932
Story type: [UI]
Summary
CatalogManagementListPage, the admin catalog-management page with three PatternFly Tabs (Clusters, Virtual Machines, Bare Metal), wired into the existing/admin/catalogroute from OSAC-2931CatalogItemCards with a scope badge (General/Organization/Project), publication status, and a publish/unpublish toggleCatalogItemCard,CatalogItemListSection) with optional admin-only slots — fully backward-compatible with the existing tenant-facingCatalogPageChanges
API hooks (
libs/ui-components/src/api/v1/)useAdminClusterCatalogItems,useAdminComputeInstanceCatalogItems,useAdminBareMetalInstanceCatalogItems— select the public or private API client based onuseSession().roleuseAdminSet*CatalogItemPublishedmutation hooks per kind, sendingupdateMask: { paths: ['published'] }ApiRouteentries to isolate the admin private-client query cache from the tenant-facing public-client cacheShared types/components (
libs/ui-components/src/components/catalog/)CatalogItemwidened to includeosac.private.v1.*types alongside the existing public onescatalogItemScope()+SHARED_TENANTconstant to derive scope (General/Organization/Project) for both roles — resolves the design doc's own unresolved "scope visibility in public API responses" open question by usingmetadata.tenantCatalogItemIconwidened to render the correct icon for private-v1 kindsCatalogItemCardgained optionalscopeBadge/statusLabel/publishToggleslots;CatalogItemListSectiongained an optionalrenderCardAddonspropNew admin components (
libs/ui-components/src/components/catalogManagement/)CatalogItemScopeBadge,CatalogItemStatusLabel,CatalogItemPublishTogglePage (
libs/ui-components/src/pages/admin/)CatalogManagementListPage+CatalogManagementTabPanel(per-tab toolbar, gallery, empty state)apps/app-frontend/src/shell/AdminCatalogRoutes.tsx: wired the new page into the previously-placeholder index routeKnown limitation (not fixable in this repo)
The public catalog-items
ListRPC infulfillment-serviceunconditionally filters topublished = truefor every caller — verified directly against the backend source, no role check exists. This means a Tenant Admin's "Unpublished"/"All" filter won't show unpublished items, and unpublishing their own item via the new toggle makes it disappear from this page entirely. The osac-ui implementation here is correct against the documented design; the backend doesn't yet honor it. Filed as OSAC-3121.Testing
catalogItemScope()'s full decision table, icon rendering for all 6 kinds, new component behavior (including a click-isolation/CSS-stacking-order regression test for the publish toggle), and full end-to-end page behavior (tab switching with independent per-tab data, search, publication filtering, navigation, empty state)Test plan
pnpm run typecheckpassespnpm run lintpasses (ESLint + Prettier + i18n sync)pnpm testpasses (390/391 — 1 pre-existing, unrelated flaky test inClusterCreatePage.test.tsx, confirmed to pass in isolation and untouched by this PR)kind-devas CSP AdminAcceptance Criteria
CatalogItemCardcards (same layout as the tenantCatalogPage)filterCatalogItemsBySearch()Assisted-by: Claude Code noreply@anthropic.com
Summary by CodeRabbit
the main difference here is in the option to publish or unpublish global catalog items.
the tenant admin can publish/unpublish only tenant scope catalog items