diff --git a/apps/app-frontend/src/shell/AdminCatalogRoutes.tsx b/apps/app-frontend/src/shell/AdminCatalogRoutes.tsx
deleted file mode 100644
index fad49d9f..00000000
--- a/apps/app-frontend/src/shell/AdminCatalogRoutes.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Route, Routes } from 'react-router-dom';
-
-import CatalogManagementListPage from '@osac/ui-components/pages/admin/CatalogManagementListPage';
-
-export const AdminCatalogRoutes = () => {
- return (
-
- } />
- } />
- } />
- } />
-
- );
-};
diff --git a/apps/app-frontend/src/shell/AppShell.tsx b/apps/app-frontend/src/shell/AppShell.tsx
index 980405d5..d6375dcd 100644
--- a/apps/app-frontend/src/shell/AppShell.tsx
+++ b/apps/app-frontend/src/shell/AppShell.tsx
@@ -15,7 +15,6 @@ import { ClusterRoutes } from '@osac/ui-components/pages/tenant/ClusterRoutes';
import { VmCreatePage } from '@osac/ui-components/pages/tenant/VmCreatePage';
import { VmListPage } from '@osac/ui-components/pages/tenant/VmListPage';
-import { AdminCatalogRoutes } from './AdminCatalogRoutes';
import { ShellMasthead } from './ShellMasthead';
import { defaultRouteForRole } from './shellRoutes';
import { ShellSidebar } from './ShellSidebar';
@@ -119,17 +118,6 @@ export const AppShell = ({ logout }: { logout: () => Promise }) => {
}
/>
- {(role === 'providerAdmin' || role === 'tenantAdmin') && (
-
-
-
- }
- />
- )}
-
} />
diff --git a/apps/app-frontend/src/shell/shellNav.test.ts b/apps/app-frontend/src/shell/shellNav.test.ts
index 93b27fdb..0b20b0ed 100644
--- a/apps/app-frontend/src/shell/shellNav.test.ts
+++ b/apps/app-frontend/src/shell/shellNav.test.ts
@@ -6,7 +6,6 @@ import { tIdentity } from '@osac/ui-components/test-utils/i18n';
import { navRowsForRole } from './shellNav';
const roles: DemoShellRole[] = ['tenantUser', 'tenantAdmin', 'providerAdmin'];
-const adminRoles: DemoShellRole[] = ['tenantAdmin', 'providerAdmin'];
const findSection = (role: DemoShellRole, sectionId: string) =>
navRowsForRole(role, tIdentity).find((row) => row.sectionId === sectionId);
@@ -37,19 +36,9 @@ describe('navRowsForRole', () => {
}
});
- it('includes Administration section with Catalog management for admin roles', () => {
- for (const role of adminRoles) {
- const admin = findSection(role, 'nav-administration');
- expect(admin).toBeDefined();
- expect(admin?.label).toBe('Administration');
- expect(admin?.children).toEqual([
- { id: 'catalog-management', label: 'Catalog management', path: '/admin/catalog' },
- ]);
+ it('does not include an Administration section for any role', () => {
+ for (const role of roles) {
+ expect(findSection(role, 'nav-administration')).toBeUndefined();
}
});
-
- it('does not include Administration section for tenantUser', () => {
- const admin = findSection('tenantUser', 'nav-administration');
- expect(admin).toBeUndefined();
- });
});
diff --git a/apps/app-frontend/src/shell/shellNav.ts b/apps/app-frontend/src/shell/shellNav.ts
index 62d1adc2..ab03c83f 100644
--- a/apps/app-frontend/src/shell/shellNav.ts
+++ b/apps/app-frontend/src/shell/shellNav.ts
@@ -45,19 +45,4 @@ const getBaseNav = (t: TFunction): NavRow[] => [
},
];
-export const navRowsForRole = (role: DemoShellRole, t: TFunction): NavRow[] => {
- const rows = getBaseNav(t);
-
- if (role === 'providerAdmin' || role === 'tenantAdmin') {
- rows.push({
- kind: 'section',
- sectionId: 'nav-administration',
- label: t('Administration'),
- children: [
- { id: 'catalog-management', label: t('Catalog management'), path: '/admin/catalog' },
- ],
- });
- }
-
- return rows;
-};
+export const navRowsForRole = (_role: DemoShellRole, t: TFunction): NavRow[] => getBaseNav(t);
diff --git a/libs/i18n/locales/en/translation.json b/libs/i18n/locales/en/translation.json
index 8ed870df..9859b3f8 100644
--- a/libs/i18n/locales/en/translation.json
+++ b/libs/i18n/locales/en/translation.json
@@ -4,7 +4,6 @@
"Add": "Add",
"Add node set": "Add node set",
"Add rule": "Add rule",
- "Administration": "Administration",
"All": "All",
"API URL": "API URL",
"At least one CIDR (IPv4 or IPv6) is required": "At least one CIDR (IPv4 or IPv6) is required",
@@ -22,8 +21,6 @@
"Catalog": "Catalog",
"Catalog configuration is unavailable for this virtual machine.": "Catalog configuration is unavailable for this virtual machine.",
"Catalog item": "Catalog item",
- "Catalog management": "Catalog management",
- "Catalog management resource type tabs": "Catalog management resource type tabs",
"catalogProvision.actions.back": "Back",
"catalogProvision.actions.cancel": "Cancel",
"catalogProvision.actions.create": "Create",
@@ -158,7 +155,6 @@
"Failed to load security groups": "Failed to load security groups",
"Failed to load subnets": "Failed to load subnets",
"Filter bare metal instances by name": "Filter bare metal instances by name",
- "Filter by publication status": "Filter by publication status",
"Filter catalog by keyword": "Filter catalog by keyword",
"Filter catalog by resource type": "Filter catalog by resource type",
"Fixed": "Fixed",
@@ -201,8 +197,6 @@
"No bare metal instances match your search.": "No bare metal instances match your search.",
"No bare metal instances yet.": "No bare metal instances yet.",
"No catalog items found": "No catalog items found",
- "No catalog items have been created yet.": "No catalog items have been created yet.",
- "No catalog items match your search or filter.": "No catalog items match your search or filter.",
"No catalog items match your search.": "No catalog items match your search.",
"No inbound rules yet. Add one to allow incoming traffic.": "No inbound rules yet. Add one to allow incoming traffic.",
"No node sets added yet.": "No node sets added yet.",
@@ -221,8 +215,6 @@
"Nodes": "Nodes",
"Open catalog item details for {{title}}": "Open catalog item details for {{title}}",
"Optional cloud-init user data (max 64 KB).": "Optional cloud-init user data (max 64 KB).",
- "Organization": "Organization",
- "Organization: {{name}}": "Organization: {{name}}",
"Outbound Rules": "Outbound Rules",
"Overview": "Overview",
"Parent virtual network": "Parent virtual network",
@@ -240,7 +232,6 @@
"Port To": "Port To",
"Port To is required for TCP/UDP": "Port To is required for TCP/UDP",
"Port To must be >= Port From": "Port To must be >= Port From",
- "Project: {{name}}": "Project: {{name}}",
"Protocol": "Protocol",
"Protocol is required": "Protocol is required",
"Provision a bare metal instance from a catalog item.": "Provision a bare metal instance from a catalog item.",
@@ -249,7 +240,6 @@
"Provisioning failed": "Provisioning failed",
"Public IP": "Public IP",
"Public SSH key is required": "Public SSH key is required",
- "Published": "Published",
"Pull secret": "Pull secret",
"Pull secret is required": "Pull secret is required",
"Pull secrets download OpenShift components and connect clusters to your Red Hat account. Copy the full JSON from OpenShift Cluster Manager (console.redhat.com/openshift/install/pull-secret).": "Pull secrets download OpenShift components and connect clusters to your Red Hat account. Copy the full JSON from OpenShift Cluster Manager (console.redhat.com/openshift/install/pull-secret).",
@@ -299,7 +289,6 @@
"UDP": "UDP",
"Unauthorized": "Unauthorized",
"Unknown": "Unknown",
- "Unpublished": "Unpublished",
"Use IPv4 CIDR notation (for example 10.128.0.0/14).": "Use IPv4 CIDR notation (for example 10.128.0.0/14).",
"Use IPv4 CIDR notation (for example 172.30.0.0/16).": "Use IPv4 CIDR notation (for example 172.30.0.0/16).",
"User data": "User data",
diff --git a/libs/ui-components/src/api/types.ts b/libs/ui-components/src/api/types.ts
index ccd54911..0356a7c5 100644
--- a/libs/ui-components/src/api/types.ts
+++ b/libs/ui-components/src/api/types.ts
@@ -25,9 +25,6 @@ export type ApiRoute =
| 'v1/baremetal_instances'
| 'v1/public_ips'
| 'v1/public_ip_attachments'
- | 'v1/private/compute_instance_catalog_items'
- | 'v1/private/cluster_catalog_items'
- | 'v1/private/baremetal_instance_catalog_items'
| 'v1/console_sessions';
/**
diff --git a/libs/ui-components/src/api/v1/baremetal-instance.test.ts b/libs/ui-components/src/api/v1/baremetal-instance.test.ts
index fe0e21ca..2201a48f 100644
--- a/libs/ui-components/src/api/v1/baremetal-instance.test.ts
+++ b/libs/ui-components/src/api/v1/baremetal-instance.test.ts
@@ -4,47 +4,11 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { act, renderHook, waitFor } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
-import type { BareMetalInstanceCatalogItem } from '@osac/types';
-import {
- BareMetalInstanceCatalogItems,
- BareMetalInstanceRunStrategy,
- BareMetalInstances,
-} from '@osac/types';
+import { BareMetalInstanceRunStrategy, BareMetalInstances } from '@osac/types';
-import {
- type PatchBareMetalInstanceInput,
- useBareMetalInstanceCatalogItems,
- usePatchBareMetalInstance,
-} from './baremetal-instance';
-import { createCatalogHookTests } from '../../test-utils/catalogHookTestHelpers';
+import { type PatchBareMetalInstanceInput, usePatchBareMetalInstance } from './baremetal-instance';
import { ApiProvider } from '../api-context';
-const item: BareMetalInstanceCatalogItem = {
- $typeName: 'osac.public.v1.BareMetalInstanceCatalogItem',
- id: 'public-1',
- title: 'Public bare metal item',
- description: '',
- template: '',
- published: true,
- fieldDefinitions: [],
-};
-
-describe('useBareMetalInstanceCatalogItems', () => {
- createCatalogHookTests({
- endpointDescription: 'public BareMetalInstanceCatalogItems',
- useHook: useBareMetalInstanceCatalogItems,
- role: 'tenantAdmin',
- item,
- registerList: (router, onList) =>
- router.service(BareMetalInstanceCatalogItems, {
- list: () => {
- onList?.();
- return { items: [item] };
- },
- }),
- });
-});
-
const makeBmi = (id: string) => ({
id,
metadata: { name: `bmi-${id}` },
diff --git a/libs/ui-components/src/api/v1/baremetal-instance.ts b/libs/ui-components/src/api/v1/baremetal-instance.ts
index 7cd547e3..00b5d1ac 100644
--- a/libs/ui-components/src/api/v1/baremetal-instance.ts
+++ b/libs/ui-components/src/api/v1/baremetal-instance.ts
@@ -9,9 +9,9 @@ import {
} from '@osac/types';
import { useApiFetch } from '../api-context';
-import { type ListParams, apiQueryKey } from '../types';
-import { type ApiQueryClient, useApiQuery, useApiQueryClient } from '../use-api-query';
+import { apiQueryKey } from '../types';
import { buildUpdateMaskPaths } from './update-mask';
+import { type ApiQueryClient, useApiQuery, useApiQueryClient } from '../use-api-query';
export const useBareMetalInstances = () => {
const client = useApiFetch(BareMetalInstances);
@@ -32,11 +32,11 @@ export const useBareMetalInstance = (id: string) => {
});
};
-export const useBareMetalInstanceCatalogItems = (params: ListParams = {}, enabled = true) => {
+export const useBareMetalInstanceCatalogItems = (enabled = true) => {
const client = useApiFetch(BareMetalInstanceCatalogItems);
return useApiQuery({
- queryKey: apiQueryKey('v1/baremetal_instance_catalog_items', undefined, params),
- queryFn: () => client.list(params),
+ queryKey: apiQueryKey('v1/baremetal_instance_catalog_items'),
+ queryFn: () => client.list({}),
select: (data) => data.items,
enabled,
});
diff --git a/libs/ui-components/src/api/v1/cluster-catalog-item.test.ts b/libs/ui-components/src/api/v1/cluster-catalog-item.test.ts
deleted file mode 100644
index 43cb0967..00000000
--- a/libs/ui-components/src/api/v1/cluster-catalog-item.test.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { describe } from 'vitest';
-
-import type { ClusterCatalogItem } from '@osac/types';
-import { ClusterCatalogItems } from '@osac/types';
-
-import { useClusterCatalogItems } from './cluster-catalog-item';
-import { createCatalogHookTests } from '../../test-utils/catalogHookTestHelpers';
-
-const item: ClusterCatalogItem = {
- $typeName: 'osac.public.v1.ClusterCatalogItem',
- id: 'public-1',
- title: 'Public cluster item',
- description: '',
- template: '',
- published: true,
- fieldDefinitions: [],
-};
-
-describe('useClusterCatalogItems', () => {
- createCatalogHookTests({
- endpointDescription: 'public ClusterCatalogItems',
- useHook: useClusterCatalogItems,
- role: 'tenantAdmin',
- item,
- registerList: (router, onList) =>
- router.service(ClusterCatalogItems, {
- list: () => {
- onList?.();
- return { items: [item] };
- },
- }),
- });
-});
diff --git a/libs/ui-components/src/api/v1/compute-instance-catalog-item.test.ts b/libs/ui-components/src/api/v1/compute-instance-catalog-item.test.ts
deleted file mode 100644
index 4af1d13b..00000000
--- a/libs/ui-components/src/api/v1/compute-instance-catalog-item.test.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { describe } from 'vitest';
-
-import type { ComputeInstanceCatalogItem } from '@osac/types';
-import { ComputeInstanceCatalogItems } from '@osac/types';
-
-import { useComputeInstanceCatalogItems } from './compute-instance-catalog-item';
-import { createCatalogHookTests } from '../../test-utils/catalogHookTestHelpers';
-
-const item: ComputeInstanceCatalogItem = {
- $typeName: 'osac.public.v1.ComputeInstanceCatalogItem',
- id: 'public-1',
- title: 'Public VM item',
- description: '',
- template: '',
- published: true,
- fieldDefinitions: [],
-};
-
-describe('useComputeInstanceCatalogItems', () => {
- createCatalogHookTests({
- endpointDescription: 'public ComputeInstanceCatalogItems',
- useHook: useComputeInstanceCatalogItems,
- role: 'tenantAdmin',
- item,
- registerList: (router, onList) =>
- router.service(ComputeInstanceCatalogItems, {
- list: () => {
- onList?.();
- return { items: [item] };
- },
- }),
- });
-});
diff --git a/libs/ui-components/src/api/v1/private/baremetal-instance-catalog-item.test.ts b/libs/ui-components/src/api/v1/private/baremetal-instance-catalog-item.test.ts
deleted file mode 100644
index 1f0b0b89..00000000
--- a/libs/ui-components/src/api/v1/private/baremetal-instance-catalog-item.test.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { describe } from 'vitest';
-
-import type { BareMetalInstanceCatalogItem } from '@osac/types/private';
-import { BareMetalInstanceCatalogItems } from '@osac/types/private';
-
-import { usePrivateBareMetalInstanceCatalogItems } from './baremetal-instance-catalog-item';
-import { createCatalogHookTests } from '../../../test-utils/catalogHookTestHelpers';
-
-const item: BareMetalInstanceCatalogItem = {
- $typeName: 'osac.private.v1.BareMetalInstanceCatalogItem',
- id: 'private-1',
- title: 'Private bare metal item',
- description: '',
- template: '',
- published: true,
- tenant: 'acme-corp',
- fieldDefinitions: [],
-};
-
-describe('usePrivateBareMetalInstanceCatalogItems', () => {
- createCatalogHookTests({
- endpointDescription: 'private BareMetalInstanceCatalogItems',
- useHook: usePrivateBareMetalInstanceCatalogItems,
- role: 'providerAdmin',
- item,
- registerList: (router, onList) =>
- router.service(BareMetalInstanceCatalogItems, {
- list: () => {
- onList?.();
- return { items: [item] };
- },
- }),
- });
-});
diff --git a/libs/ui-components/src/api/v1/private/baremetal-instance-catalog-item.ts b/libs/ui-components/src/api/v1/private/baremetal-instance-catalog-item.ts
deleted file mode 100644
index fcb209c2..00000000
--- a/libs/ui-components/src/api/v1/private/baremetal-instance-catalog-item.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { BareMetalInstanceCatalogItems } from '@osac/types/private';
-
-import { useApiFetch } from '../../api-context';
-import { type ListParams, apiQueryKey } from '../../types';
-import { useApiQuery } from '../../use-api-query';
-
-export const usePrivateBareMetalInstanceCatalogItems = (
- params: ListParams = {},
- enabled = true,
-) => {
- const client = useApiFetch(BareMetalInstanceCatalogItems);
- return useApiQuery({
- queryKey: apiQueryKey('v1/private/baremetal_instance_catalog_items', undefined, params),
- queryFn: () => client.list(params),
- select: (data) => data.items,
- enabled,
- });
-};
diff --git a/libs/ui-components/src/api/v1/private/cluster-catalog-item.test.ts b/libs/ui-components/src/api/v1/private/cluster-catalog-item.test.ts
deleted file mode 100644
index 52da0d79..00000000
--- a/libs/ui-components/src/api/v1/private/cluster-catalog-item.test.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { describe } from 'vitest';
-
-import type { ClusterCatalogItem } from '@osac/types/private';
-import { ClusterCatalogItems } from '@osac/types/private';
-
-import { usePrivateClusterCatalogItems } from './cluster-catalog-item';
-import { createCatalogHookTests } from '../../../test-utils/catalogHookTestHelpers';
-
-const item: ClusterCatalogItem = {
- $typeName: 'osac.private.v1.ClusterCatalogItem',
- id: 'private-1',
- title: 'Private cluster item',
- description: '',
- template: '',
- published: true,
- tenant: 'acme-corp',
- fieldDefinitions: [],
-};
-
-describe('usePrivateClusterCatalogItems', () => {
- createCatalogHookTests({
- endpointDescription: 'private ClusterCatalogItems',
- useHook: usePrivateClusterCatalogItems,
- role: 'providerAdmin',
- item,
- registerList: (router, onList) =>
- router.service(ClusterCatalogItems, {
- list: () => {
- onList?.();
- return { items: [item] };
- },
- }),
- });
-});
diff --git a/libs/ui-components/src/api/v1/private/cluster-catalog-item.ts b/libs/ui-components/src/api/v1/private/cluster-catalog-item.ts
deleted file mode 100644
index b02f2035..00000000
--- a/libs/ui-components/src/api/v1/private/cluster-catalog-item.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { ClusterCatalogItems } from '@osac/types/private';
-
-import { useApiFetch } from '../../api-context';
-import { type ListParams, apiQueryKey } from '../../types';
-import { useApiQuery } from '../../use-api-query';
-
-export const usePrivateClusterCatalogItems = (params: ListParams = {}, enabled = true) => {
- const client = useApiFetch(ClusterCatalogItems);
- return useApiQuery({
- queryKey: apiQueryKey('v1/private/cluster_catalog_items', undefined, params),
- queryFn: () => client.list(params),
- select: (data) => data.items,
- enabled,
- });
-};
diff --git a/libs/ui-components/src/api/v1/private/compute-instance-catalog-item.test.ts b/libs/ui-components/src/api/v1/private/compute-instance-catalog-item.test.ts
deleted file mode 100644
index cbdad046..00000000
--- a/libs/ui-components/src/api/v1/private/compute-instance-catalog-item.test.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { describe } from 'vitest';
-
-import type { ComputeInstanceCatalogItem } from '@osac/types/private';
-import { ComputeInstanceCatalogItems } from '@osac/types/private';
-
-import { usePrivateComputeInstanceCatalogItems } from './compute-instance-catalog-item';
-import { createCatalogHookTests } from '../../../test-utils/catalogHookTestHelpers';
-
-const item: ComputeInstanceCatalogItem = {
- $typeName: 'osac.private.v1.ComputeInstanceCatalogItem',
- id: 'private-1',
- title: 'Private VM item',
- description: '',
- template: '',
- published: true,
- tenant: 'acme-corp',
- fieldDefinitions: [],
-};
-
-describe('usePrivateComputeInstanceCatalogItems', () => {
- createCatalogHookTests({
- endpointDescription: 'private ComputeInstanceCatalogItems',
- useHook: usePrivateComputeInstanceCatalogItems,
- role: 'providerAdmin',
- item,
- registerList: (router, onList) =>
- router.service(ComputeInstanceCatalogItems, {
- list: () => {
- onList?.();
- return { items: [item] };
- },
- }),
- });
-});
diff --git a/libs/ui-components/src/api/v1/private/compute-instance-catalog-item.ts b/libs/ui-components/src/api/v1/private/compute-instance-catalog-item.ts
deleted file mode 100644
index 70741ca2..00000000
--- a/libs/ui-components/src/api/v1/private/compute-instance-catalog-item.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { ComputeInstanceCatalogItems } from '@osac/types/private';
-
-import { useApiFetch } from '../../api-context';
-import { type ListParams, apiQueryKey } from '../../types';
-import { useApiQuery } from '../../use-api-query';
-
-export const usePrivateComputeInstanceCatalogItems = (params: ListParams = {}, enabled = true) => {
- const client = useApiFetch(ComputeInstanceCatalogItems);
- return useApiQuery({
- queryKey: apiQueryKey('v1/private/compute_instance_catalog_items', undefined, params),
- queryFn: () => client.list(params),
- select: (data) => data.items,
- enabled,
- });
-};
diff --git a/libs/ui-components/src/components/catalog/CatalogItemCard.test.tsx b/libs/ui-components/src/components/catalog/CatalogItemCard.test.tsx
deleted file mode 100644
index 14c2d2e5..00000000
--- a/libs/ui-components/src/components/catalog/CatalogItemCard.test.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { screen } from '@testing-library/react';
-import { describe, expect, it, vi } from 'vitest';
-
-import type { ClusterCatalogItem } from '@osac/types';
-
-import CatalogItemCard from './CatalogItemCard';
-import { renderWithProviders } from '../../test-utils/TestProviders';
-import CatalogItemScopeBadge from '../catalogManagement/CatalogItemScopeBadge';
-import CatalogItemStatusLabel from '../catalogManagement/CatalogItemStatusLabel';
-
-const item: ClusterCatalogItem = {
- $typeName: 'osac.public.v1.ClusterCatalogItem',
- id: 'catalog-1',
- title: 'OpenShift 4 cluster',
- description: 'Standard OpenShift cluster offering',
- template: '',
- published: true,
- fieldDefinitions: [],
-};
-
-describe('CatalogItemCard', () => {
- it('omits scope badge and status label by default (tenant mode)', () => {
- renderWithProviders( {}} />);
- expect(screen.queryByText('General')).not.toBeInTheDocument();
- expect(screen.queryByText('Published')).not.toBeInTheDocument();
- });
-
- it('renders scope badge and status label when provided (admin mode)', () => {
- renderWithProviders(
- {}}
- scopeBadge={}
- statusLabel={}
- />,
- );
- expect(screen.getByText('General')).toBeInTheDocument();
- expect(screen.getByText('Published')).toBeInTheDocument();
- });
-
- it('navigates to details when the card is clicked', async () => {
- const onOpenDetails = vi.fn();
- const { user } = renderWithProviders(
- ,
- );
-
- await user.click(
- screen.getByRole('button', { name: `Open catalog item details for ${item.title}` }),
- );
-
- expect(onOpenDetails).toHaveBeenCalled();
- });
-});
diff --git a/libs/ui-components/src/components/catalog/CatalogItemCard.tsx b/libs/ui-components/src/components/catalog/CatalogItemCard.tsx
index 8357146e..147cb3a2 100644
--- a/libs/ui-components/src/components/catalog/CatalogItemCard.tsx
+++ b/libs/ui-components/src/components/catalog/CatalogItemCard.tsx
@@ -33,8 +33,6 @@ interface CatalogItemCardProps {
selection?: CatalogItemCardSelection;
onOpenDetails?: () => void;
isSelected?: boolean;
- scopeBadge?: React.ReactNode;
- statusLabel?: React.ReactNode;
}
const CatalogItemCard = ({
@@ -43,8 +41,6 @@ const CatalogItemCard = ({
selection,
onOpenDetails,
isSelected,
- scopeBadge,
- statusLabel,
}: CatalogItemCardProps) => {
const { t } = useTranslation();
const resources = catalogItemResourceParts(item);
@@ -107,14 +103,6 @@ const CatalogItemCard = ({
{subtitle}
- {scopeBadge || statusLabel ? (
-
-
- {scopeBadge ? {scopeBadge} : null}
- {statusLabel ? {statusLabel} : null}
-
-
- ) : null}
{resources.length > 0 ? (
diff --git a/libs/ui-components/src/components/catalog/catalogItemDisplay.test.ts b/libs/ui-components/src/components/catalog/catalogItemDisplay.test.ts
index a9a45dfa..f877f9cd 100644
--- a/libs/ui-components/src/components/catalog/catalogItemDisplay.test.ts
+++ b/libs/ui-components/src/components/catalog/catalogItemDisplay.test.ts
@@ -1,14 +1,10 @@
import { describe, expect, it } from 'vitest';
import { ClusterCatalogItem } from '@osac/types';
-import type { ClusterCatalogItem as PrivateClusterCatalogItem } from '@osac/types/private';
import {
- SHARED_TENANT,
catalogItemResourceLine,
catalogItemResourceParts,
- catalogItemScope,
- catalogItemSubtitle,
filterCatalogItemsBySearch,
} from './catalogItemDisplay';
import {
@@ -224,119 +220,3 @@ describe('filterCatalogItemsBySearch', () => {
expect(filterCatalogItemsBySearch(items, 'PRODUCTION')).toEqual([items[1]]);
});
});
-
-const basePrivateMetadata = (): NonNullable => ({
- $typeName: 'osac.private.v1.Metadata',
- finalizers: [],
- creator: 'admin',
- tenant: '',
- name: 'catalog-item',
- labels: {},
- annotations: {},
- version: 1,
- project: '',
-});
-
-const privateClusterItem = (
- overrides: Partial = {},
-): PrivateClusterCatalogItem => ({
- $typeName: 'osac.private.v1.ClusterCatalogItem',
- id: 'catalog-cluster-1',
- metadata: basePrivateMetadata(),
- title: 'OpenShift 4 cluster',
- description: 'Standard OpenShift cluster offering',
- template: 'tpl-openshift-4',
- published: true,
- tenant: '',
- fieldDefinitions: [],
- ...overrides,
-});
-
-const publicVmItemWithMetadata = (tenant: string, project = '') => ({
- $typeName: 'osac.public.v1.ComputeInstanceCatalogItem' as const,
- id: 'catalog-rhel-9',
- metadata: {
- $typeName: 'osac.public.v1.Metadata' as const,
- name: 'catalog-rhel-9',
- annotations: {},
- creator: 'foo',
- labels: {},
- project,
- tenant,
- version: 1,
- },
- title: 'RHEL 9 catalog',
- description: 'RHEL 9 base image',
- template: 'tpl-rhel-9',
- published: true,
- fieldDefinitions: [],
-});
-
-describe('catalogItemScope', () => {
- it('returns general for a CSP Admin item with no private tenant', () => {
- const item = privateClusterItem({ tenant: '' });
- expect(catalogItemScope(item, 'providerAdmin')).toEqual({ level: 'general' });
- });
-
- it('returns general for the providerAdmin role given a public-shaped item lacking a tenant field', () => {
- const item = publicVmItemWithMetadata('acme-corp');
- expect(catalogItemScope(item, 'providerAdmin')).toEqual({ level: 'general' });
- });
-
- it('returns organization with the tenant name for a CSP Admin item scoped to a tenant', () => {
- const item = privateClusterItem({ tenant: 'acme-corp' });
- expect(catalogItemScope(item, 'providerAdmin')).toEqual({
- level: 'organization',
- name: 'acme-corp',
- });
- });
-
- it('returns project for a CSP Admin item even when the private tenant is also set', () => {
- const item = privateClusterItem({
- tenant: 'acme-corp',
- metadata: { ...basePrivateMetadata(), project: 'frontend' },
- });
- expect(catalogItemScope(item, 'providerAdmin')).toEqual({
- level: 'project',
- name: 'frontend',
- });
- });
-
- it('returns general for a Tenant Admin item whose metadata.tenant is the shared sentinel', () => {
- const item = publicVmItemWithMetadata(SHARED_TENANT);
- expect(catalogItemScope(item, 'tenantAdmin')).toEqual({ level: 'general' });
- });
-
- it('returns general for a Tenant Admin item whose metadata.tenant is empty', () => {
- const item = publicVmItemWithMetadata('');
- expect(catalogItemScope(item, 'tenantAdmin')).toEqual({ level: 'general' });
- });
-
- it('returns organization for a Tenant Admin item whose metadata.tenant is not the shared sentinel', () => {
- const item = publicVmItemWithMetadata('acme-corp');
- expect(catalogItemScope(item, 'tenantAdmin')).toEqual({ level: 'organization' });
- });
-
- it('returns project for a Tenant Admin item with a project set, regardless of metadata.tenant', () => {
- const item = publicVmItemWithMetadata(SHARED_TENANT, 'frontend');
- expect(catalogItemScope(item, 'tenantAdmin')).toEqual({ level: 'project', name: 'frontend' });
- });
-});
-
-describe('existing display helpers with private-v1 items', () => {
- it('catalogItemSubtitle falls back to metadata.name when description is empty', () => {
- const item = privateClusterItem({ description: '' });
- expect(catalogItemSubtitle(item)).toBe('catalog-item');
- });
-
- it('catalogItemSubtitle uses the description when present', () => {
- const item = privateClusterItem();
- expect(catalogItemSubtitle(item)).toBe('Standard OpenShift cluster offering');
- });
-
- it('filterCatalogItemsBySearch matches a private-v1 item by title', () => {
- const item = privateClusterItem();
- expect(filterCatalogItemsBySearch([item], 'openshift')).toEqual([item]);
- expect(filterCatalogItemsBySearch([item], 'no-such-term')).toEqual([]);
- });
-});
diff --git a/libs/ui-components/src/components/catalog/catalogItemDisplay.ts b/libs/ui-components/src/components/catalog/catalogItemDisplay.ts
index cdedf9ee..ece6cacc 100644
--- a/libs/ui-components/src/components/catalog/catalogItemDisplay.ts
+++ b/libs/ui-components/src/components/catalog/catalogItemDisplay.ts
@@ -3,13 +3,7 @@ import type {
ClusterCatalogItem,
ComputeInstanceCatalogItem,
} from '@osac/types';
-import type {
- BareMetalInstanceCatalogItem as PrivateBareMetalInstanceCatalogItem,
- ClusterCatalogItem as PrivateClusterCatalogItem,
- ComputeInstanceCatalogItem as PrivateComputeInstanceCatalogItem,
-} from '@osac/types/private';
-import type { DemoShellRole } from '../../shellTypes';
import {
CATALOG_ITEM_RESOURCE_FIELD_PATHS,
type CatalogFieldDefinition,
@@ -25,15 +19,7 @@ import {
export type CatalogItem =
| ClusterCatalogItem
| BareMetalInstanceCatalogItem
- | ComputeInstanceCatalogItem
- | PrivateClusterCatalogItem
- | PrivateBareMetalInstanceCatalogItem
- | PrivateComputeInstanceCatalogItem;
-
-type PrivateCatalogItem =
- | PrivateClusterCatalogItem
- | PrivateBareMetalInstanceCatalogItem
- | PrivateComputeInstanceCatalogItem;
+ | ComputeInstanceCatalogItem;
export type CatalogItemKind = 'vm' | 'cluster' | 'bm';
@@ -155,18 +141,6 @@ export const filterCatalogItemsBySearch = (items: CatalogItem[], search: string)
return items.filter((item) => searchableCatalogItemText(item).includes(searchTerm));
};
-export type PublicationFilter = 'all' | 'published' | 'unpublished';
-
-export const matchesPublicationFilter = (item: CatalogItem, filter: PublicationFilter): boolean => {
- if (filter === 'published') {
- return item.published;
- }
- if (filter === 'unpublished') {
- return !item.published;
- }
- return true;
-};
-
export const formatCatalogFieldDefault = (def: CatalogFieldDefinition): string => {
const defaultValue = resolvedFieldDefault(def);
if (defaultValue === undefined) {
@@ -174,34 +148,3 @@ export const formatCatalogFieldDefault = (def: CatalogFieldDefinition): string =
}
return fieldDefinitionDefaultToInputString(defaultValue) || '—';
};
-
-/**
- * fulfillment-service's built-in global tenant. Every object without an explicit tenant is
- * auto-assigned this value server-side, and it round-trips unmasked through the public API's
- * `metadata.tenant` field even though the business `tenant` field is stripped from public catalog
- * item responses entirely.
- */
-export const SHARED_TENANT = 'shared';
-
-export type CatalogItemScope =
- | { level: 'general' }
- | { level: 'organization'; name?: string }
- | { level: 'project'; name: string };
-
-const isPrivateCatalogItem = (item: CatalogItem): item is PrivateCatalogItem =>
- item.$typeName.startsWith('osac.private.');
-
-export const catalogItemScope = (item: CatalogItem, role: DemoShellRole): CatalogItemScope => {
- const project = item.metadata?.project ?? '';
- if (project) {
- return { level: 'project', name: project };
- }
- if (role === 'providerAdmin') {
- const tenant = isPrivateCatalogItem(item) ? item.tenant : '';
- return tenant ? { level: 'organization', name: tenant } : { level: 'general' };
- }
- const metadataTenant = item.metadata?.tenant ?? '';
- return metadataTenant === SHARED_TENANT || !metadataTenant
- ? { level: 'general' }
- : { level: 'organization' };
-};
diff --git a/libs/ui-components/src/components/catalogManagement/CatalogItemScopeBadge.test.tsx b/libs/ui-components/src/components/catalogManagement/CatalogItemScopeBadge.test.tsx
deleted file mode 100644
index e0f9d2a0..00000000
--- a/libs/ui-components/src/components/catalogManagement/CatalogItemScopeBadge.test.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { screen } from '@testing-library/react';
-import { describe, expect, it } from 'vitest';
-
-import CatalogItemScopeBadge from './CatalogItemScopeBadge';
-import { renderWithProviders } from '../../test-utils/TestProviders';
-
-describe('CatalogItemScopeBadge', () => {
- it('renders "General" in blue for general scope', () => {
- renderWithProviders();
- const label = screen.getByText('General');
- expect(label.closest('.pf-v6-c-label')).toHaveClass('pf-m-blue');
- });
-
- it('renders "Organization: {name}" in purple when a tenant name is known', () => {
- renderWithProviders(
- ,
- );
- const label = screen.getByText('Organization: acme-corp');
- expect(label.closest('.pf-v6-c-label')).toHaveClass('pf-m-purple');
- });
-
- it('renders plain "Organization" in purple when no tenant name is known', () => {
- renderWithProviders();
- const label = screen.getByText('Organization');
- expect(label.closest('.pf-v6-c-label')).toHaveClass('pf-m-purple');
- });
-
- it('renders "Project: {name}" in teal for project scope', () => {
- renderWithProviders();
- const label = screen.getByText('Project: frontend');
- expect(label.closest('.pf-v6-c-label')).toHaveClass('pf-m-teal');
- });
-});
diff --git a/libs/ui-components/src/components/catalogManagement/CatalogItemScopeBadge.tsx b/libs/ui-components/src/components/catalogManagement/CatalogItemScopeBadge.tsx
deleted file mode 100644
index 2ad04e81..00000000
--- a/libs/ui-components/src/components/catalogManagement/CatalogItemScopeBadge.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Label } from '@patternfly/react-core';
-
-import { useTranslation } from '../../hooks/useTranslation';
-import type { CatalogItemScope } from '../catalog/catalogItemDisplay';
-
-interface CatalogItemScopeBadgeProps {
- scope: CatalogItemScope;
-}
-
-const CatalogItemScopeBadge = ({ scope }: CatalogItemScopeBadgeProps) => {
- const { t } = useTranslation();
-
- switch (scope.level) {
- case 'general':
- return ;
- case 'organization':
- return (
-
- );
- case 'project':
- return ;
- default: {
- // Guards against a future scope level being added without updating this switch — TS flags the
- // assignment below at compile time, while runtime still renders a safe fallback instead of crashing.
- const exhaustiveCheck: never = scope;
- void exhaustiveCheck;
- return ;
- }
- }
-};
-
-export default CatalogItemScopeBadge;
diff --git a/libs/ui-components/src/components/catalogManagement/CatalogItemStatusLabel.test.tsx b/libs/ui-components/src/components/catalogManagement/CatalogItemStatusLabel.test.tsx
deleted file mode 100644
index bc4b622c..00000000
--- a/libs/ui-components/src/components/catalogManagement/CatalogItemStatusLabel.test.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { screen } from '@testing-library/react';
-import { describe, expect, it } from 'vitest';
-
-import CatalogItemStatusLabel from './CatalogItemStatusLabel';
-import { renderWithProviders } from '../../test-utils/TestProviders';
-
-describe('CatalogItemStatusLabel', () => {
- it('renders "Published" in green when published', () => {
- renderWithProviders();
- const label = screen.getByText('Published');
- expect(label.closest('.pf-v6-c-label')).toHaveClass('pf-m-green');
- });
-
- it('renders "Unpublished" in grey when not published', () => {
- renderWithProviders();
- const label = screen.getByText('Unpublished');
- expect(label.closest('.pf-v6-c-label')).not.toHaveClass('pf-m-green');
- });
-});
diff --git a/libs/ui-components/src/components/catalogManagement/CatalogItemStatusLabel.tsx b/libs/ui-components/src/components/catalogManagement/CatalogItemStatusLabel.tsx
deleted file mode 100644
index d721561e..00000000
--- a/libs/ui-components/src/components/catalogManagement/CatalogItemStatusLabel.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Label } from '@patternfly/react-core';
-
-import { useTranslation } from '../../hooks/useTranslation';
-
-interface CatalogItemStatusLabelProps {
- published: boolean;
-}
-
-const CatalogItemStatusLabel = ({ published }: CatalogItemStatusLabelProps) => {
- const { t } = useTranslation();
-
- return published ? (
-
- ) : (
-
- );
-};
-
-export default CatalogItemStatusLabel;
diff --git a/libs/ui-components/src/icons.tsx b/libs/ui-components/src/icons.tsx
index 2f52364b..a4e859f3 100644
--- a/libs/ui-components/src/icons.tsx
+++ b/libs/ui-components/src/icons.tsx
@@ -1,7 +1,6 @@
import type { ComponentType } from 'react';
import type { SVGIconProps } from '@patternfly/react-icons/dist/esm/createIcon';
import CloudIcon from '@patternfly/react-icons/dist/esm/icons/cloud-icon';
-import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon';
import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
import NetworkIcon from '@patternfly/react-icons/dist/esm/icons/network-icon';
import ServerIcon from '@patternfly/react-icons/dist/esm/icons/server-icon';
@@ -13,7 +12,6 @@ const SHELL_NAV_ICONS: Record> = {
clusters: CloudIcon,
'bare-metal': ServerIcon,
'virtual-networks': NetworkIcon,
- 'catalog-management': CogIcon,
};
export const shellNavIcon = (itemId: string) => {
@@ -25,21 +23,16 @@ interface CatalogItemIconProps {
kind:
| 'osac.public.v1.ClusterCatalogItem'
| 'osac.public.v1.BareMetalInstanceCatalogItem'
- | 'osac.public.v1.ComputeInstanceCatalogItem'
- | 'osac.private.v1.ClusterCatalogItem'
- | 'osac.private.v1.BareMetalInstanceCatalogItem'
- | 'osac.private.v1.ComputeInstanceCatalogItem';
+ | 'osac.public.v1.ComputeInstanceCatalogItem';
}
export const CatalogItemIcon = ({ kind }: CatalogItemIconProps) => {
let Icon = VirtualMachineIcon;
switch (kind) {
case 'osac.public.v1.ClusterCatalogItem':
- case 'osac.private.v1.ClusterCatalogItem':
Icon = CloudIcon;
break;
case 'osac.public.v1.BareMetalInstanceCatalogItem':
- case 'osac.private.v1.BareMetalInstanceCatalogItem':
Icon = ServerIcon;
break;
default:
diff --git a/libs/ui-components/src/pages/admin/BareMetalInstanceCatalogManagementPanel.tsx b/libs/ui-components/src/pages/admin/BareMetalInstanceCatalogManagementPanel.tsx
deleted file mode 100644
index b2b2edee..00000000
--- a/libs/ui-components/src/pages/admin/BareMetalInstanceCatalogManagementPanel.tsx
+++ /dev/null
@@ -1,162 +0,0 @@
-import { useNavigate } from 'react-router-dom';
-import {
- Button,
- EmptyState,
- EmptyStateBody,
- Flex,
- FlexItem,
- Gallery,
- GalleryItem,
- SearchInput,
- Stack,
- StackItem,
- Title,
- ToggleGroup,
- ToggleGroupItem,
-} from '@patternfly/react-core';
-
-import { useBareMetalInstanceCatalogItems } from '@osac/ui-components/api/v1/baremetal-instance';
-import { usePrivateBareMetalInstanceCatalogItems } from '@osac/ui-components/api/v1/private/baremetal-instance-catalog-item';
-import CatalogItemCard from '@osac/ui-components/components/catalog/CatalogItemCard';
-import {
- type PublicationFilter,
- catalogItemScope,
- filterCatalogItemsBySearch,
- matchesPublicationFilter,
-} from '@osac/ui-components/components/catalog/catalogItemDisplay';
-import CatalogItemScopeBadge from '@osac/ui-components/components/catalogManagement/CatalogItemScopeBadge';
-import CatalogItemStatusLabel from '@osac/ui-components/components/catalogManagement/CatalogItemStatusLabel';
-import ListPageBody from '@osac/ui-components/components/Page/ListPageBody';
-import { useTranslation } from '@osac/ui-components/hooks/useTranslation';
-import type { DemoShellRole } from '@osac/ui-components/shellTypes';
-
-interface BareMetalInstanceCatalogManagementPanelProps {
- isActive: boolean;
- search: string;
- setSearch: (value: string) => void;
- publicationFilter: PublicationFilter;
- setPublicationFilter: (value: PublicationFilter) => void;
- role: DemoShellRole;
-}
-
-const BareMetalInstanceCatalogManagementPanel = ({
- isActive,
- search,
- setSearch,
- publicationFilter,
- setPublicationFilter,
- role,
-}: BareMetalInstanceCatalogManagementPanelProps) => {
- const { t } = useTranslation();
- const navigate = useNavigate();
- const isProviderAdmin = role === 'providerAdmin';
- const publicResult = useBareMetalInstanceCatalogItems(undefined, isActive && !isProviderAdmin);
- const privateResult = usePrivateBareMetalInstanceCatalogItems(
- undefined,
- isActive && isProviderAdmin,
- );
- const { data = [], isLoading, error, isSuccess } = isProviderAdmin ? privateResult : publicResult;
-
- const filteredItems = filterCatalogItemsBySearch(data, search).filter((item) =>
- matchesPublicationFilter(item, publicationFilter),
- );
-
- const publicationFilters: ReadonlyArray<{ value: PublicationFilter; label: string }> = [
- { value: 'all', label: t('All') },
- { value: 'published', label: t('Published') },
- { value: 'unpublished', label: t('Unpublished') },
- ];
-
- const isFiltered = search.trim().length > 0 || publicationFilter !== 'all';
- const showEmptyState = isSuccess && !error && filteredItems.length === 0;
-
- return (
-
-
-
-
-
-
- setSearch(value)}
- onClear={() => setSearch('')}
- aria-label={t('Filter catalog by keyword')}
- isDisabled={isLoading || !!error}
- />
-
-
-
- {publicationFilters.map((option) => (
- setPublicationFilter(option.value)}
- />
- ))}
-
-
-
-
-
-
-
-
-
- {showEmptyState ? (
-
-
-
- {isFiltered
- ? t('No catalog items match your search or filter.')
- : t('No catalog items have been created yet.')}
-
-
-
- ) : (
-
-
-
-
- {t('Bare Metal')}
-
-
-
-
- {filteredItems.map((item) => (
-
- navigate(`/admin/catalog/baremetal-instance/${item.id}`)}
- scopeBadge={}
- statusLabel={}
- />
-
- ))}
-
-
-
-
- )}
-
- );
-};
-
-export default BareMetalInstanceCatalogManagementPanel;
diff --git a/libs/ui-components/src/pages/admin/CatalogManagementListPage.test.tsx b/libs/ui-components/src/pages/admin/CatalogManagementListPage.test.tsx
deleted file mode 100644
index c1b55bc0..00000000
--- a/libs/ui-components/src/pages/admin/CatalogManagementListPage.test.tsx
+++ /dev/null
@@ -1,234 +0,0 @@
-import { Route, Routes } from 'react-router-dom';
-import { createRouterTransport } from '@connectrpc/connect';
-import { screen, waitFor } from '@testing-library/react';
-import { describe, expect, it } from 'vitest';
-
-import type { ClusterCatalogItem, ComputeInstanceCatalogItem } from '@osac/types';
-import {
- BareMetalInstanceCatalogItems,
- ClusterCatalogItems,
- ComputeInstanceCatalogItems,
-} from '@osac/types';
-import type {
- ClusterCatalogItem as PrivateClusterCatalogItem,
- ComputeInstanceCatalogItem as PrivateComputeInstanceCatalogItem,
-} from '@osac/types/private';
-import {
- BareMetalInstanceCatalogItems as PrivateBareMetalInstanceCatalogItems,
- ClusterCatalogItems as PrivateClusterCatalogItems,
- ComputeInstanceCatalogItems as PrivateComputeInstanceCatalogItems,
-} from '@osac/types/private';
-
-import CatalogManagementListPage from './CatalogManagementListPage';
-import { SessionProvider } from '../../hooks/use-session';
-import { renderWithProviders } from '../../test-utils/TestProviders';
-
-const privateClusterItem: PrivateClusterCatalogItem = {
- $typeName: 'osac.private.v1.ClusterCatalogItem',
- id: 'cluster-private-1',
- title: 'OpenShift 4 cluster',
- description: '',
- template: '',
- published: true,
- tenant: 'acme-corp',
- fieldDefinitions: [],
-};
-
-const publicClusterItem: ClusterCatalogItem = {
- $typeName: 'osac.public.v1.ClusterCatalogItem',
- id: 'cluster-public-1',
- title: 'Shared OpenShift cluster',
- description: '',
- template: '',
- published: true,
- fieldDefinitions: [],
- metadata: {
- $typeName: 'osac.public.v1.Metadata',
- name: 'shared-cluster',
- annotations: {},
- creator: 'admin',
- labels: {},
- project: '',
- tenant: 'shared',
- version: 1,
- },
-};
-
-const publicUnpublishedVmItem: ComputeInstanceCatalogItem = {
- $typeName: 'osac.public.v1.ComputeInstanceCatalogItem',
- id: 'vm-public-1',
- title: 'Fedora workstation',
- description: '',
- template: '',
- published: false,
- fieldDefinitions: [],
- metadata: {
- $typeName: 'osac.public.v1.Metadata',
- name: 'fedora',
- annotations: {},
- creator: 'tenant-admin',
- labels: {},
- project: '',
- tenant: 'acme-corp',
- version: 1,
- },
-};
-
-const privateVmItem: PrivateComputeInstanceCatalogItem = {
- $typeName: 'osac.private.v1.ComputeInstanceCatalogItem',
- id: 'vm-private-1',
- title: 'RHEL 9 workstation',
- description: '',
- template: '',
- published: true,
- tenant: 'acme-corp',
- fieldDefinitions: [],
-};
-
-const emptyList = () => ({ items: [] });
-
-const createTestTransport = () =>
- createRouterTransport((router) => {
- router.service(PrivateClusterCatalogItems, { list: () => ({ items: [privateClusterItem] }) });
- router.service(ClusterCatalogItems, { list: () => ({ items: [publicClusterItem] }) });
- router.service(PrivateComputeInstanceCatalogItems, {
- list: () => ({ items: [privateVmItem] }),
- });
- router.service(ComputeInstanceCatalogItems, {
- list: () => ({ items: [publicUnpublishedVmItem] }),
- });
- router.service(PrivateBareMetalInstanceCatalogItems, { list: emptyList });
- router.service(BareMetalInstanceCatalogItems, { list: emptyList });
- });
-
-const renderPage = (role: 'providerAdmin' | 'tenantAdmin', transport = createTestTransport()) =>
- renderWithProviders(
-
-
- } />
- create-page} />
- detail-page} />
-
- ,
- { transport, routerEntries: ['/admin/catalog'] },
- );
-
-describe('CatalogManagementListPage', () => {
- it('renders the three resource type tabs', () => {
- renderPage('providerAdmin');
- expect(screen.getByRole('tab', { name: 'Clusters' })).toBeInTheDocument();
- expect(screen.getByRole('tab', { name: 'Virtual Machines' })).toBeInTheDocument();
- expect(screen.getByRole('tab', { name: 'Bare Metal' })).toBeInTheDocument();
- });
-
- it('shows the CSP Admin (private API) items with an organization scope badge on the default tab', async () => {
- renderPage('providerAdmin');
- await waitFor(() => {
- expect(screen.getByText(privateClusterItem.title)).toBeInTheDocument();
- });
- expect(screen.getByText('Organization: acme-corp')).toBeInTheDocument();
- });
-
- it('shows the Tenant Admin (public API) items on the default tab', async () => {
- renderPage('tenantAdmin');
- await waitFor(() => {
- expect(screen.getByText(publicClusterItem.title)).toBeInTheDocument();
- });
- expect(screen.getByText('General')).toBeInTheDocument();
- });
-
- it('switches tabs and shows the newly active tab items', async () => {
- const { user } = renderPage('tenantAdmin');
-
- await waitFor(() => {
- expect(screen.getByText(publicClusterItem.title)).toBeInTheDocument();
- });
-
- await user.click(screen.getByRole('tab', { name: 'Virtual Machines' }));
-
- await waitFor(() => {
- expect(screen.getByText(publicUnpublishedVmItem.title)).toBeInTheDocument();
- });
- });
-
- it('shows an empty state on a tab with no catalog items', async () => {
- const { user } = renderPage('providerAdmin');
-
- await waitFor(() => {
- expect(screen.getByText(privateClusterItem.title)).toBeInTheDocument();
- });
-
- await user.click(screen.getByRole('tab', { name: 'Bare Metal' }));
-
- await waitFor(() => {
- expect(
- screen.getByRole('heading', { name: 'No catalog items found', level: 2 }),
- ).toBeInTheDocument();
- });
- expect(screen.getByText('No catalog items have been created yet.')).toBeInTheDocument();
- });
-
- it('filters items by search keyword', async () => {
- const { user } = renderPage('providerAdmin');
-
- await waitFor(() => {
- expect(screen.getByText(privateClusterItem.title)).toBeInTheDocument();
- });
-
- await user.type(
- screen.getByRole('textbox', { name: 'Filter catalog by keyword' }),
- 'no-such-item',
- );
-
- await waitFor(() => {
- expect(screen.queryByText(privateClusterItem.title)).not.toBeInTheDocument();
- });
- });
-
- it('filters items by publication status', async () => {
- const { user } = renderPage('tenantAdmin');
-
- await user.click(screen.getByRole('tab', { name: 'Virtual Machines' }));
- await waitFor(() => {
- expect(screen.getByText(publicUnpublishedVmItem.title)).toBeInTheDocument();
- });
-
- await user.click(screen.getByRole('button', { name: 'Published' }));
-
- await waitFor(() => {
- expect(screen.queryByText(publicUnpublishedVmItem.title)).not.toBeInTheDocument();
- });
- });
-
- it('navigates to the kind-specific create route when Create is clicked', async () => {
- const { user } = renderPage('providerAdmin');
-
- await waitFor(() => {
- expect(screen.getByText(privateClusterItem.title)).toBeInTheDocument();
- });
-
- await user.click(screen.getByRole('button', { name: 'Create' }));
-
- await waitFor(() => {
- expect(screen.getByText('create-page')).toBeInTheDocument();
- });
- });
-
- it('navigates to the detail route when a card is clicked', async () => {
- const { user } = renderPage('providerAdmin');
-
- await waitFor(() => {
- expect(screen.getByText(privateClusterItem.title)).toBeInTheDocument();
- });
-
- await user.click(
- screen.getByRole('button', {
- name: `Open catalog item details for ${privateClusterItem.title}`,
- }),
- );
-
- await waitFor(() => {
- expect(screen.getByText('detail-page')).toBeInTheDocument();
- });
- });
-});
diff --git a/libs/ui-components/src/pages/admin/CatalogManagementListPage.tsx b/libs/ui-components/src/pages/admin/CatalogManagementListPage.tsx
deleted file mode 100644
index 79e2a508..00000000
--- a/libs/ui-components/src/pages/admin/CatalogManagementListPage.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { useState } from 'react';
-import { Tab, TabTitleText, Tabs } from '@patternfly/react-core';
-
-import { type PublicationFilter } from '@osac/ui-components/components/catalog/catalogItemDisplay';
-import ListPage from '@osac/ui-components/components/Page/ListPage';
-import { useSession } from '@osac/ui-components/hooks/use-session';
-import { useTranslation } from '@osac/ui-components/hooks/useTranslation';
-
-import BareMetalInstanceCatalogManagementPanel from './BareMetalInstanceCatalogManagementPanel';
-import ClusterCatalogManagementPanel from './ClusterCatalogManagementPanel';
-import ComputeInstanceCatalogManagementPanel from './ComputeInstanceCatalogManagementPanel';
-
-type CatalogManagementTabKey = 'cluster' | 'compute-instance' | 'baremetal-instance';
-
-const CatalogManagementListPage = () => {
- const { t } = useTranslation();
- const { role } = useSession();
- const [activeTab, setActiveTab] = useState('cluster');
- const [search, setSearch] = useState('');
- const [publicationFilter, setPublicationFilter] = useState('all');
-
- const sharedPanelProps = { search, setSearch, publicationFilter, setPublicationFilter, role };
-
- return (
-
- setActiveTab(eventKey as CatalogManagementTabKey)}
- aria-label={t('Catalog management resource type tabs')}
- >
- {t('Clusters')}}>
-
-
- {t('Virtual Machines')}}
- >
-
-
- {t('Bare Metal')}}>
-
-
-
-
- );
-};
-
-export default CatalogManagementListPage;
diff --git a/libs/ui-components/src/pages/admin/ClusterCatalogManagementPanel.tsx b/libs/ui-components/src/pages/admin/ClusterCatalogManagementPanel.tsx
deleted file mode 100644
index 30212044..00000000
--- a/libs/ui-components/src/pages/admin/ClusterCatalogManagementPanel.tsx
+++ /dev/null
@@ -1,159 +0,0 @@
-import { useNavigate } from 'react-router-dom';
-import {
- Button,
- EmptyState,
- EmptyStateBody,
- Flex,
- FlexItem,
- Gallery,
- GalleryItem,
- SearchInput,
- Stack,
- StackItem,
- Title,
- ToggleGroup,
- ToggleGroupItem,
-} from '@patternfly/react-core';
-
-import { useClusterCatalogItems } from '@osac/ui-components/api/v1/cluster-catalog-item';
-import { usePrivateClusterCatalogItems } from '@osac/ui-components/api/v1/private/cluster-catalog-item';
-import CatalogItemCard from '@osac/ui-components/components/catalog/CatalogItemCard';
-import {
- type PublicationFilter,
- catalogItemScope,
- filterCatalogItemsBySearch,
- matchesPublicationFilter,
-} from '@osac/ui-components/components/catalog/catalogItemDisplay';
-import CatalogItemScopeBadge from '@osac/ui-components/components/catalogManagement/CatalogItemScopeBadge';
-import CatalogItemStatusLabel from '@osac/ui-components/components/catalogManagement/CatalogItemStatusLabel';
-import ListPageBody from '@osac/ui-components/components/Page/ListPageBody';
-import { useTranslation } from '@osac/ui-components/hooks/useTranslation';
-import type { DemoShellRole } from '@osac/ui-components/shellTypes';
-
-interface ClusterCatalogManagementPanelProps {
- isActive: boolean;
- search: string;
- setSearch: (value: string) => void;
- publicationFilter: PublicationFilter;
- setPublicationFilter: (value: PublicationFilter) => void;
- role: DemoShellRole;
-}
-
-const ClusterCatalogManagementPanel = ({
- isActive,
- search,
- setSearch,
- publicationFilter,
- setPublicationFilter,
- role,
-}: ClusterCatalogManagementPanelProps) => {
- const { t } = useTranslation();
- const navigate = useNavigate();
- const isProviderAdmin = role === 'providerAdmin';
- const publicResult = useClusterCatalogItems(undefined, isActive && !isProviderAdmin);
- const privateResult = usePrivateClusterCatalogItems(undefined, isActive && isProviderAdmin);
- const { data = [], isLoading, error, isSuccess } = isProviderAdmin ? privateResult : publicResult;
-
- const filteredItems = filterCatalogItemsBySearch(data, search).filter((item) =>
- matchesPublicationFilter(item, publicationFilter),
- );
-
- const publicationFilters: ReadonlyArray<{ value: PublicationFilter; label: string }> = [
- { value: 'all', label: t('All') },
- { value: 'published', label: t('Published') },
- { value: 'unpublished', label: t('Unpublished') },
- ];
-
- const isFiltered = search.trim().length > 0 || publicationFilter !== 'all';
- // `isSuccess` (not just `!isLoading`) guards against a disabled, not-yet-fetched query on an
- // inactive tab — those report `isLoading: false` with no data, which would otherwise show this
- // tab as empty before it has ever actually fetched.
- const showEmptyState = isSuccess && !error && filteredItems.length === 0;
-
- return (
-
-
-
-
-
-
- setSearch(value)}
- onClear={() => setSearch('')}
- aria-label={t('Filter catalog by keyword')}
- isDisabled={isLoading || !!error}
- />
-
-
-
- {publicationFilters.map((option) => (
- setPublicationFilter(option.value)}
- />
- ))}
-
-
-
-
-
-
-
-
-
- {showEmptyState ? (
-
-
-
- {isFiltered
- ? t('No catalog items match your search or filter.')
- : t('No catalog items have been created yet.')}
-
-
-
- ) : (
-
-
-
-
- {t('Clusters')}
-
-
-
-
- {filteredItems.map((item) => (
-
- navigate(`/admin/catalog/cluster/${item.id}`)}
- scopeBadge={}
- statusLabel={}
- />
-
- ))}
-
-
-
-
- )}
-
- );
-};
-
-export default ClusterCatalogManagementPanel;
diff --git a/libs/ui-components/src/pages/admin/ComputeInstanceCatalogManagementPanel.tsx b/libs/ui-components/src/pages/admin/ComputeInstanceCatalogManagementPanel.tsx
deleted file mode 100644
index 16d2e9b4..00000000
--- a/libs/ui-components/src/pages/admin/ComputeInstanceCatalogManagementPanel.tsx
+++ /dev/null
@@ -1,162 +0,0 @@
-import { useNavigate } from 'react-router-dom';
-import {
- Button,
- EmptyState,
- EmptyStateBody,
- Flex,
- FlexItem,
- Gallery,
- GalleryItem,
- SearchInput,
- Stack,
- StackItem,
- Title,
- ToggleGroup,
- ToggleGroupItem,
-} from '@patternfly/react-core';
-
-import { useComputeInstanceCatalogItems } from '@osac/ui-components/api/v1/compute-instance-catalog-item';
-import { usePrivateComputeInstanceCatalogItems } from '@osac/ui-components/api/v1/private/compute-instance-catalog-item';
-import CatalogItemCard from '@osac/ui-components/components/catalog/CatalogItemCard';
-import {
- type PublicationFilter,
- catalogItemScope,
- filterCatalogItemsBySearch,
- matchesPublicationFilter,
-} from '@osac/ui-components/components/catalog/catalogItemDisplay';
-import CatalogItemScopeBadge from '@osac/ui-components/components/catalogManagement/CatalogItemScopeBadge';
-import CatalogItemStatusLabel from '@osac/ui-components/components/catalogManagement/CatalogItemStatusLabel';
-import ListPageBody from '@osac/ui-components/components/Page/ListPageBody';
-import { useTranslation } from '@osac/ui-components/hooks/useTranslation';
-import type { DemoShellRole } from '@osac/ui-components/shellTypes';
-
-interface ComputeInstanceCatalogManagementPanelProps {
- isActive: boolean;
- search: string;
- setSearch: (value: string) => void;
- publicationFilter: PublicationFilter;
- setPublicationFilter: (value: PublicationFilter) => void;
- role: DemoShellRole;
-}
-
-const ComputeInstanceCatalogManagementPanel = ({
- isActive,
- search,
- setSearch,
- publicationFilter,
- setPublicationFilter,
- role,
-}: ComputeInstanceCatalogManagementPanelProps) => {
- const { t } = useTranslation();
- const navigate = useNavigate();
- const isProviderAdmin = role === 'providerAdmin';
- const publicResult = useComputeInstanceCatalogItems(undefined, isActive && !isProviderAdmin);
- const privateResult = usePrivateComputeInstanceCatalogItems(
- undefined,
- isActive && isProviderAdmin,
- );
- const { data = [], isLoading, error, isSuccess } = isProviderAdmin ? privateResult : publicResult;
-
- const filteredItems = filterCatalogItemsBySearch(data, search).filter((item) =>
- matchesPublicationFilter(item, publicationFilter),
- );
-
- const publicationFilters: ReadonlyArray<{ value: PublicationFilter; label: string }> = [
- { value: 'all', label: t('All') },
- { value: 'published', label: t('Published') },
- { value: 'unpublished', label: t('Unpublished') },
- ];
-
- const isFiltered = search.trim().length > 0 || publicationFilter !== 'all';
- const showEmptyState = isSuccess && !error && filteredItems.length === 0;
-
- return (
-
-
-
-
-
-
- setSearch(value)}
- onClear={() => setSearch('')}
- aria-label={t('Filter catalog by keyword')}
- isDisabled={isLoading || !!error}
- />
-
-
-
- {publicationFilters.map((option) => (
- setPublicationFilter(option.value)}
- />
- ))}
-
-
-
-
-
-
-
-
-
- {showEmptyState ? (
-
-
-
- {isFiltered
- ? t('No catalog items match your search or filter.')
- : t('No catalog items have been created yet.')}
-
-
-
- ) : (
-
-
-
-
- {t('Virtual Machines')}
-
-
-
-
- {filteredItems.map((item) => (
-
- navigate(`/admin/catalog/compute-instance/${item.id}`)}
- scopeBadge={}
- statusLabel={}
- />
-
- ))}
-
-
-
-
- )}
-
- );
-};
-
-export default ComputeInstanceCatalogManagementPanel;
diff --git a/libs/ui-components/src/pages/tenant/CatalogPage.tsx b/libs/ui-components/src/pages/tenant/CatalogPage.tsx
index de610e71..a817fa7e 100644
--- a/libs/ui-components/src/pages/tenant/CatalogPage.tsx
+++ b/libs/ui-components/src/pages/tenant/CatalogPage.tsx
@@ -48,7 +48,7 @@ const getTypeLabel = (typeFilter: CatalogTypeFilter, t: TFunction) => {
const useCatalogItems = (typeFilter: CatalogTypeFilter) => {
const vms = useComputeInstanceCatalogItems(undefined, typeFilter === 'vm');
const clusters = useClusterCatalogItems(undefined, typeFilter === 'cluster');
- const bms = useBareMetalInstanceCatalogItems(undefined, typeFilter === 'bm');
+ const bms = useBareMetalInstanceCatalogItems(typeFilter === 'bm');
switch (typeFilter) {
case 'vm':
diff --git a/libs/ui-components/src/test-utils/TestProviders.tsx b/libs/ui-components/src/test-utils/TestProviders.tsx
index 3163a3dd..404d4338 100644
--- a/libs/ui-components/src/test-utils/TestProviders.tsx
+++ b/libs/ui-components/src/test-utils/TestProviders.tsx
@@ -3,7 +3,7 @@ import { I18nextProvider } from 'react-i18next';
import { MemoryRouter } from 'react-router-dom';
import type { Transport } from '@connectrpc/connect';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { type RenderOptions, type RenderResult, render, renderHook } from '@testing-library/react';
+import { type RenderOptions, type RenderResult, render } from '@testing-library/react';
import type { UserEvent } from '@testing-library/user-event';
import userEvent from '@testing-library/user-event';
import i18n from 'i18next';
@@ -15,8 +15,6 @@ import {
} from './createMockConnectTransport';
import en from '../../../i18n/locales/en/translation.json';
import { ApiProvider } from '../api/api-context';
-import { SessionProvider } from '../hooks/use-session';
-import type { DemoShellRole } from '../shellTypes';
const createTestI18n = () => {
const instance = i18n.createInstance();
@@ -98,28 +96,3 @@ export const renderWithProviders = (
return { ...view, user: userEvent.setup() };
};
-
-export type RenderHookWithProvidersOptions = {
- role: DemoShellRole;
- transport: Transport;
- username?: string;
-};
-
-/** Renders a hook wrapped in `SessionProvider` + `ApiProvider` + a fresh `QueryClient` — for testing
- * role-aware hooks (e.g. admin catalog-item hooks) against a mock Connect transport. */
-export const renderHookWithProviders = (
- hook: () => TResult,
- { role, transport, username = 'test-user' }: RenderHookWithProvidersOptions,
-) => {
- const queryClient = new QueryClient({
- defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
- });
- const wrapper = ({ children }: { children: ReactNode }) => (
-
-
- {children}
-
-
- );
- return { ...renderHook(hook, { wrapper }), queryClient };
-};
diff --git a/libs/ui-components/src/test-utils/catalogHookTestHelpers.ts b/libs/ui-components/src/test-utils/catalogHookTestHelpers.ts
deleted file mode 100644
index 7a29f934..00000000
--- a/libs/ui-components/src/test-utils/catalogHookTestHelpers.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { createRouterTransport } from '@connectrpc/connect';
-import type { ConnectRouter } from '@connectrpc/connect';
-import type { UseQueryResult } from '@tanstack/react-query';
-import { waitFor } from '@testing-library/react';
-import { expect, it } from 'vitest';
-
-import { renderHookWithProviders } from './TestProviders';
-import type { ListParams } from '../api/types';
-import type { DemoShellRole } from '../shellTypes';
-
-interface CatalogHookTestConfig {
- /** Human-readable endpoint description used in the generated test name, e.g. "public ClusterCatalogItems". */
- endpointDescription: string;
- useHook: (params?: ListParams, enabled?: boolean) => UseQueryResult;
- role: DemoShellRole;
- item: TItem;
- /** Registers the mock service on the router; call `onList` when the List RPC is invoked. */
- registerList: (router: ConnectRouter, onList?: () => void) => void;
-}
-
-/**
- * Shared "fetches items from the List endpoint" + "does not fetch when disabled" test pair for the
- * per-kind catalog-item list hooks (public and private). Each call site keeps its own concretely-typed
- * `registerList` callback so the mock service registration stays fully type-checked against the real
- * Connect service descriptor.
- */
-export const createCatalogHookTests = ({
- endpointDescription,
- useHook,
- role,
- item,
- registerList,
-}: CatalogHookTestConfig) => {
- it(`fetches items from the ${endpointDescription} List endpoint`, async () => {
- const transport = createRouterTransport((router) => registerList(router));
-
- const { result } = renderHookWithProviders(() => useHook(), { role, transport });
-
- await waitFor(() => expect(result.current.isSuccess).toBe(true));
- expect(result.current.data).toEqual([item]);
- });
-
- it('does not fetch when disabled', async () => {
- let listCalled = false;
- const transport = createRouterTransport((router) =>
- registerList(router, () => {
- listCalled = true;
- }),
- );
-
- renderHookWithProviders(() => useHook({}, false), { role, transport });
-
- await new Promise((resolve) => setTimeout(resolve, 10));
- expect(listCalled).toBe(false);
- });
-};