From 77b39343b1fe979dc48d8ede84ef67dd33d08069 Mon Sep 17 00:00:00 2001 From: manaswinidas Date: Fri, 12 Apr 2024 12:52:02 +0530 Subject: [PATCH] Empty state registered models table --- frontend/src/__mocks__/mockModelRegistries.ts | 213 ++++++++++++++++++ frontend/src/__mocks__/mockModelRegistry.ts | 64 ++++++ .../src/__mocks__/mockRegisteredModelsList.ts | 6 +- .../src/__mocks__/mockRouteK8sResource.ts | 49 ++++ .../e2e/modelRegistry/ModelRegistry.cy.ts | 33 ++- .../cypress/cypress/pages/modelRegistry.ts | 14 +- .../modelRegistry/ModelRegistryEmpty.tsx | 14 -- .../screens/EmptyRegisteredModels.tsx | 28 +++ .../modelRegistry/screens/ModelRegistry.tsx | 14 +- .../screens/RegisteredModelListView.tsx | 137 +---------- .../screens/RegisteredModelsTableToolbar.tsx | 148 ++++++++++++ 11 files changed, 562 insertions(+), 158 deletions(-) create mode 100644 frontend/src/__mocks__/mockModelRegistries.ts create mode 100644 frontend/src/__mocks__/mockModelRegistry.ts delete mode 100644 frontend/src/pages/modelRegistry/ModelRegistryEmpty.tsx create mode 100644 frontend/src/pages/modelRegistry/screens/EmptyRegisteredModels.tsx create mode 100644 frontend/src/pages/modelRegistry/screens/RegisteredModelsTableToolbar.tsx diff --git a/frontend/src/__mocks__/mockModelRegistries.ts b/frontend/src/__mocks__/mockModelRegistries.ts new file mode 100644 index 0000000000..89ff2c60ce --- /dev/null +++ b/frontend/src/__mocks__/mockModelRegistries.ts @@ -0,0 +1,213 @@ +export const mockModelRegistries = { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + items: [ + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + kind: 'ModelRegistry', + metadata: { + creationTimestamp: '2024-03-19T08:16:56Z', + finalizers: ['modelregistry.opendatahub.io/finalizer'], + generation: 1, + managedFields: [ + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:spec': { + '.': {}, + 'f:grpc': { + '.': {}, + 'f:port': {}, + }, + 'f:rest': { + '.': {}, + 'f:port': {}, + 'f:serviceRoute': {}, + }, + }, + }, + manager: 'Mozilla', + operation: 'Update', + time: '2024-03-19T08:16:56Z', + }, + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:metadata': { + 'f:finalizers': { + '.': {}, + 'v:"modelregistry.opendatahub.io/finalizer"': {}, + }, + }, + }, + manager: 'manager', + operation: 'Update', + time: '2024-03-19T08:16:56Z', + }, + ], + name: 'example', + namespace: 'shared', + resourceVersion: '39722859', + uid: 'f054dd1a-53e2-4b45-bdb1-dc1a3fcf5815', + }, + spec: { + grpc: { + port: 1111, + }, + rest: { + port: 1111, + serviceRoute: 'disabled', + }, + }, + }, + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + kind: 'ModelRegistry', + metadata: { + annotations: { + 'kubectl.kubernetes.io/last-applied-configuration': + '{"apiVersion":"modelregistry.opendatahub.io/v1alpha1","kind":"ModelRegistry","metadata":{"annotations":{},"labels":{"app.kubernetes.io/created-by":"model-registry-operator","app.kubernetes.io/instance":"modelregistry-sample","app.kubernetes.io/managed-by":"kustomize","app.kubernetes.io/name":"modelregistry","app.kubernetes.io/part-of":"model-registry-operator"},"name":"modelregistry-sample","namespace":"shared"},"spec":{"grpc":{"port":9090},"postgres":{"database":"model-registry","host":"model-registry-db","passwordSecret":{"key":"database-password","name":"model-registry-db"},"username":"mlmduser"},"rest":{"port":8080,"serviceRoute":"disabled"}}}\n', + }, + creationTimestamp: '2024-03-14T08:01:42Z', + finalizers: ['modelregistry.opendatahub.io/finalizer'], + generation: 1, + labels: { + 'app.kubernetes.io/created-by': 'model-registry-operator', + 'app.kubernetes.io/instance': 'modelregistry-sample', + 'app.kubernetes.io/managed-by': 'kustomize', + 'app.kubernetes.io/name': 'modelregistry', + 'app.kubernetes.io/part-of': 'model-registry-operator', + }, + managedFields: [ + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:metadata': { + 'f:annotations': { + '.': {}, + 'f:kubectl.kubernetes.io/last-applied-configuration': {}, + }, + 'f:labels': { + '.': {}, + 'f:app.kubernetes.io/created-by': {}, + 'f:app.kubernetes.io/instance': {}, + 'f:app.kubernetes.io/managed-by': {}, + 'f:app.kubernetes.io/name': {}, + 'f:app.kubernetes.io/part-of': {}, + }, + }, + 'f:spec': { + '.': {}, + 'f:grpc': { + '.': {}, + 'f:port': {}, + }, + 'f:postgres': { + '.': {}, + 'f:database': {}, + 'f:host': {}, + 'f:passwordSecret': { + '.': {}, + 'f:key': {}, + 'f:name': {}, + }, + 'f:port': {}, + 'f:skipDBCreation': {}, + 'f:sslMode': {}, + 'f:username': {}, + }, + 'f:rest': { + '.': {}, + 'f:port': {}, + 'f:serviceRoute': {}, + }, + }, + }, + manager: 'kubectl-client-side-apply', + operation: 'Update', + time: '2024-03-14T08:01:42Z', + }, + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:metadata': { + 'f:finalizers': { + '.': {}, + 'v:"modelregistry.opendatahub.io/finalizer"': {}, + }, + }, + }, + manager: 'manager', + operation: 'Update', + time: '2024-03-14T08:11:17Z', + }, + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:status': { + '.': {}, + 'f:conditions': {}, + }, + }, + manager: 'manager', + operation: 'Update', + subresource: 'status', + time: '2024-03-22T09:30:02Z', + }, + ], + name: 'modelregistry-sample', + namespace: 'shared', + resourceVersion: '41871020', + uid: '6687fd4e-c417-43c1-92f4-7b6908541c83', + }, + spec: { + grpc: { + port: 9090, + }, + postgres: { + database: 'model-registry', + host: 'model-registry-db', + passwordSecret: { + key: 'database-password', + name: 'model-registry-db', + }, + port: 5432, + skipDBCreation: false, + sslMode: 'disable', + username: 'mlmduser', + }, + rest: { + port: 8080, + serviceRoute: 'disabled', + }, + }, + status: { + conditions: [ + { + lastTransitionTime: '2024-03-22T09:30:02Z', + message: 'Deployment for custom resource modelregistry-sample was successfully created', + reason: 'CreatedDeployment', + status: 'True', + type: 'Progressing', + }, + { + lastTransitionTime: '2024-03-14T08:11:26Z', + message: 'Deployment for custom resource modelregistry-sample is available', + reason: 'DeploymentAvailable', + status: 'True', + type: 'Available', + }, + ], + }, + }, + ], + kind: 'ModelRegistryList', + metadata: { + continue: '', + resourceVersion: '55673902', + }, +}; diff --git a/frontend/src/__mocks__/mockModelRegistry.ts b/frontend/src/__mocks__/mockModelRegistry.ts new file mode 100644 index 0000000000..135d2b9bb8 --- /dev/null +++ b/frontend/src/__mocks__/mockModelRegistry.ts @@ -0,0 +1,64 @@ +export const mockModelRegistry = { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + kind: 'ModelRegistry', + metadata: { + annotations: { + 'kubectl.kubernetes.io/last-applied-configuration': + '{"apiVersion":"modelregistry.opendatahub.io/v1alpha1","kind":"ModelRegistry","metadata":{"annotations":{},"labels":{"app.kubernetes.io/created-by":"model-registry-operator","app.kubernetes.io/instance":"modelregistry-sample","app.kubernetes.io/managed-by":"kustomize","app.kubernetes.io/name":"modelregistry","app.kubernetes.io/part-of":"model-registry-operator"},"name":"modelregistry-sample","namespace":"shared"},"spec":{"grpc":{"port":9090},"postgres":{"database":"model-registry","host":"model-registry-db","passwordSecret":{"key":"database-password","name":"model-registry-db"},"username":"mlmduser"},"rest":{"port":8080,"serviceRoute":"disabled"}}}\n', + }, + creationTimestamp: '2024-03-14T08:01:42Z', + finalizers: ['modelregistry.opendatahub.io/finalizer'], + generation: 1, + labels: { + 'app.kubernetes.io/created-by': 'model-registry-operator', + 'app.kubernetes.io/instance': 'modelregistry-sample', + 'app.kubernetes.io/managed-by': 'kustomize', + 'app.kubernetes.io/name': 'modelregistry', + 'app.kubernetes.io/part-of': 'model-registry-operator', + }, + managedFields: [], + name: 'modelregistry-sample', + namespace: 'shared', + resourceVersion: '41871020', + uid: '6687fd4e-c417-43c1-92f4-7b6908541c83', + }, + spec: { + grpc: { + port: 9090, + }, + postgres: { + database: 'model-registry', + host: 'model-registry-db', + passwordSecret: { + key: 'database-password', + name: 'model-registry-db', + }, + port: 5432, + skipDBCreation: false, + sslMode: 'disable', + username: 'mlmduser', + }, + rest: { + port: 8080, + serviceRoute: 'disabled', + }, + }, + status: { + conditions: [ + { + lastTransitionTime: '2024-03-22T09:30:02Z', + message: 'Deployment for custom resource modelregistry-sample was successfully created', + reason: 'CreatedDeployment', + status: 'True', + type: 'Progressing', + }, + { + lastTransitionTime: '2024-03-14T08:11:26Z', + message: 'Deployment for custom resource modelregistry-sample is available', + reason: 'DeploymentAvailable', + status: 'True', + type: 'Available', + }, + ], + }, +}; diff --git a/frontend/src/__mocks__/mockRegisteredModelsList.ts b/frontend/src/__mocks__/mockRegisteredModelsList.ts index cac4e1a1ed..e040273bce 100644 --- a/frontend/src/__mocks__/mockRegisteredModelsList.ts +++ b/frontend/src/__mocks__/mockRegisteredModelsList.ts @@ -1,9 +1,11 @@ import { RegisteredModelList } from '~/concepts/modelRegistry/types'; import { mockRegisteredModel } from './mockRegisteredModel'; -export const mockRegisteredModelList = (): RegisteredModelList => ({ +export const mockRegisteredModelList = ({ + size = 2, +}: Partial): RegisteredModelList => ({ items: [mockRegisteredModel({ name: 'test-1' }), mockRegisteredModel({ name: 'test-2' })], nextPageToken: '', pageSize: 0, - size: 4, + size, }); diff --git a/frontend/src/__mocks__/mockRouteK8sResource.ts b/frontend/src/__mocks__/mockRouteK8sResource.ts index 0ed9f5e998..37392cb701 100644 --- a/frontend/src/__mocks__/mockRouteK8sResource.ts +++ b/frontend/src/__mocks__/mockRouteK8sResource.ts @@ -140,3 +140,52 @@ export const mockRouteK8sResourceModelServing = ({ ], }, }); + +export const mockModelRegistryRoute = { + kind: 'Route', + apiVersion: 'route.openshift.io/v1', + metadata: { + name: 'modelregistry-sample', + namespace: 'shared', + uid: '6fda5a9e-e75d-4d63-a1ac-15c172d4c833', + resourceVersion: '41961020', + creationTimestamp: '2024-03-22T11:35:18Z', + labels: { + app: 'modelregistry-sample', + component: 'model-registry', + }, + annotations: { + 'openshift.io/host.generated': 'true', + }, + managedFields: [], + }, + spec: { + host: 'modelregistry-sample-shared.apps.modelserving-ui.dev.datahub.redhat.com', + to: { + kind: 'Service', + name: 'modelregistry-sample', + weight: 100, + }, + port: { + targetPort: 'http-api', + }, + wildcardPolicy: 'None', + }, + status: { + ingress: [ + { + host: 'modelregistry-sample-shared.apps.modelserving-ui.dev.datahub.redhat.com', + routerName: 'default', + conditions: [ + { + type: 'Admitted', + status: 'True', + lastTransitionTime: '2024-03-22T11:35:18Z', + }, + ], + wildcardPolicy: 'None', + routerCanonicalHostname: 'router-default.apps.modelserving-ui.dev.datahub.redhat.com', + }, + ], + }, +}; diff --git a/frontend/src/__tests__/cypress/cypress/e2e/modelRegistry/ModelRegistry.cy.ts b/frontend/src/__tests__/cypress/cypress/e2e/modelRegistry/ModelRegistry.cy.ts index 74f67223f5..3e928d6025 100644 --- a/frontend/src/__tests__/cypress/cypress/e2e/modelRegistry/ModelRegistry.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/e2e/modelRegistry/ModelRegistry.cy.ts @@ -1,12 +1,17 @@ import { mockComponents } from '~/__mocks__/mockComponents'; import { mockDashboardConfig } from '~/__mocks__/mockDashboardConfig'; +import { mockModelRegistries } from '~/__mocks__/mockModelRegistries'; +import { mockRegisteredModelList } from '~/__mocks__/mockRegisteredModelsList'; +import { mockModelRegistry } from '~/__mocks__/mockModelRegistry'; import { modelRegistry } from '~/__tests__/cypress/cypress/pages/modelRegistry'; +import { mockModelRegistryRoute } from '~/__mocks__'; type HandlersProps = { disableModelRegistryFeature?: boolean; + size?: number; }; -const initIntercepts = ({ disableModelRegistryFeature = false }: HandlersProps) => { +const initIntercepts = ({ disableModelRegistryFeature = false, size = 0 }: HandlersProps) => { cy.interceptOdh( 'GET /api/config', mockDashboardConfig({ @@ -14,7 +19,22 @@ const initIntercepts = ({ disableModelRegistryFeature = false }: HandlersProps) }), ); cy.interceptOdh('GET /api/components', { query: { installed: 'true' } }, mockComponents()); + cy.intercept( + '/api/k8s/apis/modelregistry.opendatahub.io/v1alpha1/modelregistries', + mockModelRegistries, + ); + cy.intercept( + '/api/k8s/apis/modelregistry.opendatahub.io/v1alpha1/namespaces/shared/modelregistries/modelregistry-sample', + mockModelRegistry, + ); + cy.intercept( + '/api/k8s/apis/route.openshift.io/v1/namespaces/shared/routes/modelregistry-sample', + mockModelRegistryRoute, + ); + + cy.intercept('/api/model_registry/v1alpha2/registered_models', mockRegisteredModelList({ size })); }; + describe('Model Registry Global', () => { it('Model Registry Disabled in the cluster', () => { initIntercepts({ @@ -35,4 +55,15 @@ describe('Model Registry Global', () => { modelRegistry.tabEnabled(); }); + it('No registered models in the selected Model Registry', () => { + initIntercepts({ + disableModelRegistryFeature: false, + size: 0, + }); + + modelRegistry.visit(); + modelRegistry.navigate(); + modelRegistry.shouldtableToolbarExist(); + modelRegistry.shouldregisteredModelsEmpty(); + }); }); diff --git a/frontend/src/__tests__/cypress/cypress/pages/modelRegistry.ts b/frontend/src/__tests__/cypress/cypress/pages/modelRegistry.ts index 77d21c6cc2..77fc4871ac 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/modelRegistry.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/modelRegistry.ts @@ -6,8 +6,8 @@ class ModelRegistry { this.waitLanding(); } - visit(modelRegistry?: string) { - cy.visit(`/modelRegistry${modelRegistry}`); + visit() { + cy.visit(`/modelRegistry`); this.wait(); } @@ -17,7 +17,7 @@ class ModelRegistry { } private wait() { - cy.findByTestId('app-page-title').contains('Model Registry'); + cy.findByTestId('app-page-title').should('exist'); cy.testA11y(); } @@ -30,6 +30,14 @@ class ModelRegistry { return this; } + shouldregisteredModelsEmpty() { + cy.findByTestId('no-registered-models').should('exist'); + } + + shouldtableToolbarExist() { + cy.findByTestId('registered-models-table-toolbar').should('exist'); + } + tabEnabled() { appChrome.findNavItem('Model Registry').should('exist'); return this; diff --git a/frontend/src/pages/modelRegistry/ModelRegistryEmpty.tsx b/frontend/src/pages/modelRegistry/ModelRegistryEmpty.tsx deleted file mode 100644 index 0dcddbfb1e..0000000000 --- a/frontend/src/pages/modelRegistry/ModelRegistryEmpty.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import ApplicationsPage from '~/pages/ApplicationsPage'; - -const ModelRegistryEmpty: React.FC = () => ( - -); - -export default ModelRegistryEmpty; diff --git a/frontend/src/pages/modelRegistry/screens/EmptyRegisteredModels.tsx b/frontend/src/pages/modelRegistry/screens/EmptyRegisteredModels.tsx new file mode 100644 index 0000000000..e5fc317e0c --- /dev/null +++ b/frontend/src/pages/modelRegistry/screens/EmptyRegisteredModels.tsx @@ -0,0 +1,28 @@ +import { + EmptyState, + EmptyStateBody, + EmptyStateHeader, + EmptyStateIcon, + EmptyStateVariant, +} from '@patternfly/react-core'; +import { PlusCircleIcon } from '@patternfly/react-icons'; +import * as React from 'react'; + +type EmptyRegisteredModelsType = { + preferredModelRegistry?: string; +}; +const EmptyRegisteredModels: React.FC = ({ preferredModelRegistry }) => ( + + } + /> + + {preferredModelRegistry} has no models registered to it. Register a model to this +
+ registry or select a different one. +
+
+); + +export default EmptyRegisteredModels; diff --git a/frontend/src/pages/modelRegistry/screens/ModelRegistry.tsx b/frontend/src/pages/modelRegistry/screens/ModelRegistry.tsx index 2a8495409c..694fe0ed40 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelRegistry.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelRegistry.tsx @@ -2,19 +2,25 @@ import React from 'react'; import ApplicationsPage from '~/pages/ApplicationsPage'; import { ProjectObjectType } from '~/concepts/design/utils'; import TitleWithIcon from '~/concepts/design/TitleWithIcon'; -import ModelRegistryEmpty from '~/pages/modelRegistry/ModelRegistryEmpty'; import { ModelRegistryContext } from '~/concepts/modelRegistry/context/ModelRegistryContext'; import useRegisteredModels from '~/concepts/modelRegistry/apiHooks/useRegisteredModels'; import RegisteredModelListView from './RegisteredModelListView'; +import EmptyRegisteredModels from './EmptyRegisteredModels'; +import RegisteredModelsTableToolbar from './RegisteredModelsTableToolbar'; const ModelRegistry: React.FC = () => { - const { modelRegistries } = React.useContext(ModelRegistryContext); + const { preferredModelRegistry } = React.useContext(ModelRegistryContext); const [registeredModels, loaded, loadError] = useRegisteredModels(); return ( } + empty={registeredModels.size === 0} + emptyStatePage={ + <> + + + + } title={ } diff --git a/frontend/src/pages/modelRegistry/screens/RegisteredModelListView.tsx b/frontend/src/pages/modelRegistry/screens/RegisteredModelListView.tsx index 0f629f6850..0356a08885 100644 --- a/frontend/src/pages/modelRegistry/screens/RegisteredModelListView.tsx +++ b/frontend/src/pages/modelRegistry/screens/RegisteredModelListView.tsx @@ -1,23 +1,8 @@ import * as React from 'react'; -import { - Dropdown, - DropdownItem, - DropdownList, - MenuToggle, - MenuToggleAction, - MenuToggleElement, - SearchInput, - ToolbarFilter, - ToolbarGroup, - ToolbarItem, - ToolbarToggleGroup, -} from '@patternfly/react-core'; -import { EllipsisVIcon, FilterIcon } from '@patternfly/react-icons'; import { SearchType } from '~/concepts/dashboard/DashboardSearchField'; import { RegisteredModel } from '~/concepts/modelRegistry/types'; -import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; import RegisteredModelTable from './RegisteredModelTable'; -import ModelRegistrySelector from './ModelRegistrySelector'; +import RegisteredModelsTableToolbar from './RegisteredModelsTableToolbar'; type RegisteredModelListViewProps = { registeredModels: RegisteredModel[]; @@ -26,12 +11,9 @@ type RegisteredModelListViewProps = { const RegisteredModelListView: React.FC = ({ registeredModels: unfilteredRegisteredModels, }) => { - const [searchType, setSearchType] = React.useState(SearchType.KEYWORD); + const [searchType] = React.useState(SearchType.KEYWORD); const [search, setSearch] = React.useState(''); - const [isRegisterNewVersionOpen, setIsRegisterNewVersionOpen] = React.useState(false); - const [isArchivedModelKebabOpen, setIsArchivedModelKebabOpen] = React.useState(false); - const filteredRegisteredModels = unfilteredRegisteredModels.filter((rm) => { if (!search) { return true; @@ -57,124 +39,11 @@ const RegisteredModelListView: React.FC = ({ setSearch(''); }; - const searchTypes = React.useMemo(() => [SearchType.KEYWORD], []); // TODO Add owner once RHOAIENG-5066 is completed. - - const tooltipRef = React.useRef(null); - - const toggleGroupItems = ( - - setSearch('')} - deleteChipGroup={() => setSearch('')} - categoryName="Keyword" - > - ({ - key, - label: key, - }))} - value={searchType} - onChange={(newSearchType) => { - setSearchType(newSearchType as SearchType); - }} - icon={} - /> - - - { - setSearch(searchValue); - }} - onClear={() => setSearch('')} - style={{ minWidth: '200px' }} - /> - - - ); - return ( - - - - } breakpoint="xl"> - {toggleGroupItems} - - - setIsRegisterNewVersionOpen(false)} - onOpenChange={(isOpen) => setIsRegisterNewVersionOpen(isOpen)} - toggle={(toggleRef) => ( - setIsRegisterNewVersionOpen(!isRegisterNewVersionOpen)} - isExpanded={isRegisterNewVersionOpen} - splitButtonOptions={{ - variant: 'action', - items: [ - undefined} - > - Register model - , - ], - }} - aria-label="Register model toggle" - data-testid="register-model-split-button" - /> - )} - > - - undefined} - ref={tooltipRef} - isDisabled // This feature is currently disabled but will be enabled in a future PR post-summit release. - > - Register new version - - - - - - setIsArchivedModelKebabOpen(false)} - onOpenChange={(isOpen: boolean) => setIsArchivedModelKebabOpen(isOpen)} - toggle={(tr: React.Ref) => ( - setIsArchivedModelKebabOpen(!isArchivedModelKebabOpen)} - isExpanded={isArchivedModelKebabOpen} - > - - - )} - shouldFocusToggleOnSelect - > - - View archived models - - - - - } + toolbarContent={} /> ); }; diff --git a/frontend/src/pages/modelRegistry/screens/RegisteredModelsTableToolbar.tsx b/frontend/src/pages/modelRegistry/screens/RegisteredModelsTableToolbar.tsx new file mode 100644 index 0000000000..a45d08cc49 --- /dev/null +++ b/frontend/src/pages/modelRegistry/screens/RegisteredModelsTableToolbar.tsx @@ -0,0 +1,148 @@ +import * as React from 'react'; +import { + Dropdown, + DropdownItem, + DropdownList, + MenuToggle, + MenuToggleAction, + MenuToggleElement, + SearchInput, + Toolbar, + ToolbarContent, + ToolbarFilter, + ToolbarGroup, + ToolbarItem, + ToolbarToggleGroup, +} from '@patternfly/react-core'; +import { EllipsisVIcon, FilterIcon } from '@patternfly/react-icons'; +import { SearchType } from '~/concepts/dashboard/DashboardSearchField'; +import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; +import ModelRegistrySelector from './ModelRegistrySelector'; + +const RegisteredModelsTableToolbar: React.FC = () => { + const [searchType, setSearchType] = React.useState(SearchType.KEYWORD); + const [search, setSearch] = React.useState(''); + + const [isRegisterNewVersionOpen, setIsRegisterNewVersionOpen] = React.useState(false); + const [isArchivedModelKebabOpen, setIsArchivedModelKebabOpen] = React.useState(false); + + const searchTypes = React.useMemo(() => [SearchType.KEYWORD], []); // TODO Add owner once RHOAIENG-5066 is completed. + + const tooltipRef = React.useRef(null); + + const toggleGroupItems = ( + + setSearch('')} + deleteChipGroup={() => setSearch('')} + categoryName="Keyword" + > + ({ + key, + label: key, + }))} + value={searchType} + onChange={(newSearchType) => { + setSearchType(newSearchType as SearchType); + }} + icon={} + /> + + + { + setSearch(searchValue); + }} + onClear={() => setSearch('')} + style={{ minWidth: '200px' }} + /> + + + ); + + return ( + + + + + + } breakpoint="xl"> + {toggleGroupItems} + + + setIsRegisterNewVersionOpen(false)} + onOpenChange={(isOpen) => setIsRegisterNewVersionOpen(isOpen)} + toggle={(toggleRef) => ( + setIsRegisterNewVersionOpen(!isRegisterNewVersionOpen)} + isExpanded={isRegisterNewVersionOpen} + splitButtonOptions={{ + variant: 'action', + items: [ + undefined} + > + Register model + , + ], + }} + aria-label="Register model toggle" + data-testid="register-model-split-button" + /> + )} + > + + undefined} + ref={tooltipRef} + isDisabled // This feature is currently disabled but will be enabled in a future PR post-summit release. + > + Register new version + + + + + + setIsArchivedModelKebabOpen(false)} + onOpenChange={(isOpen: boolean) => setIsArchivedModelKebabOpen(isOpen)} + toggle={(tr: React.Ref) => ( + setIsArchivedModelKebabOpen(!isArchivedModelKebabOpen)} + isExpanded={isArchivedModelKebabOpen} + aria-label="View archived models" + > + + + )} + shouldFocusToggleOnSelect + > + + View archived models + + + + + + ); +}; + +export default RegisteredModelsTableToolbar;