diff --git a/frontend/packages/ceph-storage-plugin/src/utils/table-filters.ts b/frontend/packages/ceph-storage-plugin/src/utils/table-filters.ts index b46f5555612..5b20a820060 100644 --- a/frontend/packages/ceph-storage-plugin/src/utils/table-filters.ts +++ b/frontend/packages/ceph-storage-plugin/src/utils/table-filters.ts @@ -1,11 +1,12 @@ import * as _ from 'lodash'; import { TFunction } from 'i18next'; import { RowFilter } from '@console/internal/components/filter-toolbar'; +import { K8sResourceKind } from '@console/internal/module/k8s'; import { getPhase } from './noobaa-utils'; const allPhases = ['Pending', 'Bound', 'Lost']; -export const obcStatusFilter = (t: TFunction): RowFilter => ({ +export const obcStatusFilter = (t: TFunction): RowFilter => ({ type: 'obc-status', filterGroupName: t('ceph-storage-plugin~Status'), reducer: getPhase, @@ -19,12 +20,14 @@ export const obcStatusFilter = (t: TFunction): RowFilter => ({ } const phase = getPhase(obc); return ( - phases.selected.has(phase) || !_.includes(phases.all, phase) || _.isEmpty(phases.selected) + phases.selected.includes(phase) || + !_.includes(phases.all, phase) || + _.isEmpty(phases.selected) ); }, }); -export const obStatusFilter = (t: TFunction): RowFilter => ({ +export const obStatusFilter = (t: TFunction): RowFilter => ({ type: 'ob-status', filterGroupName: t('ceph-storage-plugin~Status'), reducer: getPhase, @@ -38,7 +41,9 @@ export const obStatusFilter = (t: TFunction): RowFilter => ({ } const phase = getPhase(ob); return ( - phases.selected.has(phase) || !_.includes(phases.all, phase) || _.isEmpty(phases.selected) + phases.selected.includes(phase) || + !_.includes(phases.all, phase) || + _.isEmpty(phases.selected) ); }, }); diff --git a/frontend/packages/console-app/src/components/nodes/NodeDetailsPage.tsx b/frontend/packages/console-app/src/components/nodes/NodeDetailsPage.tsx index 84373cc2ad2..ba37cdd04e5 100644 --- a/frontend/packages/console-app/src/components/nodes/NodeDetailsPage.tsx +++ b/frontend/packages/console-app/src/components/nodes/NodeDetailsPage.tsx @@ -33,7 +33,7 @@ const NodeDetailsPage: React.FC> = (pro )), events(ResourceEventStream), diff --git a/frontend/packages/console-shared/src/hooks/useK8sModel.ts b/frontend/packages/console-shared/src/hooks/useK8sModel.ts index 7190f234665..0698ea8243b 100644 --- a/frontend/packages/console-shared/src/hooks/useK8sModel.ts +++ b/frontend/packages/console-shared/src/hooks/useK8sModel.ts @@ -7,10 +7,11 @@ import { RootState } from '@console/internal/redux'; // Hook that retrieves the k8s model for provided groupVersionKind reference. Hook version of // `connectToModel`. export const useK8sModel = (groupVersionKind: GroupVersionKind): [K8sKind, boolean] => [ - useSelector( - ({ k8s }) => - k8s.getIn(['RESOURCES', 'models', groupVersionKind]) ?? - k8s.getIn(['RESOURCES', 'models', kindForReference(groupVersionKind)]), + useSelector(({ k8s }) => + groupVersionKind + ? k8s.getIn(['RESOURCES', 'models', groupVersionKind]) ?? + k8s.getIn(['RESOURCES', 'models', kindForReference(groupVersionKind)]) + : undefined, ), useSelector(({ k8s }) => k8s.getIn(['RESOURCES', 'inFlight']) ?? false), ]; diff --git a/frontend/packages/container-security/src/components/ImageVulnerabilitiesList.tsx b/frontend/packages/container-security/src/components/ImageVulnerabilitiesList.tsx index b07d5550b69..9d95157ac70 100644 --- a/frontend/packages/container-security/src/components/ImageVulnerabilitiesList.tsx +++ b/frontend/packages/container-security/src/components/ImageVulnerabilitiesList.tsx @@ -32,7 +32,7 @@ const ImageVulnerabilitiesList: React.FC = (props }, } = props; - const imageVulnerabilitiesRowFilters: RowFilter[] = [ + const imageVulnerabilitiesRowFilters: RowFilter[] = [ { filterGroupName: t('container-security~Severity'), items: [ @@ -47,7 +47,7 @@ const ImageVulnerabilitiesList: React.FC = (props type: 'vulnerability-severity', reducer: (v) => v.vulnerability.severity, filter: (filter, vuln) => - filter.selected.has(vuln.vulnerability.severity) || _.isEmpty(filter.selected), + filter.selected?.includes(vuln.vulnerability.severity) || _.isEmpty(filter.selected), }, ]; diff --git a/frontend/packages/knative-plugin/src/components/eventing/channels-list/ChannelListPage.tsx b/frontend/packages/knative-plugin/src/components/eventing/channels-list/ChannelListPage.tsx index 9f9bf958910..86d79016483 100644 --- a/frontend/packages/knative-plugin/src/components/eventing/channels-list/ChannelListPage.tsx +++ b/frontend/packages/knative-plugin/src/components/eventing/channels-list/ChannelListPage.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { MultiListPage } from '@console/internal/components/factory'; import { RowFilter } from '@console/internal/components/filter-toolbar'; -import { K8sResourceKind, referenceFor, referenceForModel } from '@console/internal/module/k8s'; +import { K8sResourceCommon, referenceFor, referenceForModel } from '@console/internal/module/k8s'; import { getDynamicChannelModel, useChannelModels, @@ -34,29 +34,24 @@ const ChannelListPage: React.FC> = (p [eventSourceChannels, modelsLoaded], ); - const getModelId = React.useCallback((obj: K8sResourceKind) => { + const getModelId = React.useCallback((obj: K8sResourceCommon) => { const reference = referenceFor(obj); const model = getDynamicChannelModel(reference); return model.id; }, []); - const rowFilterReducer = React.useCallback( - ({ selected }: { selected: Set; all: string[] }, obj: K8sResourceKind) => - selected.size === 0 || selected.has(getModelId(obj)), - [getModelId], - ); - - const channelRowFilter: RowFilter[] = React.useMemo( + const channelRowFilter = React.useMemo[]>( () => [ { filterGroupName: 'Type', type: 'event-source-type', items: eventSourceChannels.map(({ id, label }) => ({ id, title: label })), reducer: getModelId, - filter: rowFilterReducer, + filter: (filter, obj) => + !filter.selected?.length || filter.selected?.includes(getModelId(obj)), }, ], - [eventSourceChannels, getModelId, rowFilterReducer], + [eventSourceChannels, getModelId], ); return ( > : [], [sourcesModel, modelsLoaded], ); - const getModelId = React.useCallback((obj: K8sResourceKind) => { + const getModelId = React.useCallback((obj: K8sResourceCommon) => { const reference = referenceFor(obj); const model = getDynamicEventSourceModel(reference) || modelFor(reference); return model.id; }, []); - const rowFilterReducer = React.useCallback( - ({ selected }: { selected: Set; all: string[] }, obj: K8sResourceKind) => - selected.size === 0 || selected.has(getModelId(obj)), - [getModelId], - ); - - const eventSourceRowFilters: RowFilter[] = React.useMemo( + const eventSourceRowFilters = React.useMemo[]>( () => [ { filterGroupName: 'Type', type: 'event-source-type', items: sourcesModel.map(({ id, label }) => ({ id, title: label })), reducer: getModelId, - filter: rowFilterReducer, + filter: (filter, obj) => + !filter.selected?.length || filter.selected?.includes(getModelId(obj)), }, ], - [sourcesModel, getModelId, rowFilterReducer], + [sourcesModel, getModelId], ); return ( { const diskType = typeReducer(obj); return ( - disks.selected.size === 0 || disks.selected.has(diskType) || !_.includes(disks.all, diskType) + !disks.selected.length || disks.selected.includes(diskType) || disks.all?.includes(diskType) ); }, }; diff --git a/frontend/packages/kubevirt-plugin/src/components/vm-templates/vm-template.tsx b/frontend/packages/kubevirt-plugin/src/components/vm-templates/vm-template.tsx index c93bd72c3a8..88d3371c5e2 100644 --- a/frontend/packages/kubevirt-plugin/src/components/vm-templates/vm-template.tsx +++ b/frontend/packages/kubevirt-plugin/src/components/vm-templates/vm-template.tsx @@ -38,16 +38,16 @@ const filters = (t: TFunction): RowFilter[] => [ return 'user'; }, items: templateProviders(t), - filter: (types, obj: VirtualMachineTemplateBundle) => { + filter: (types, obj) => { let providerFilter = true; - if (types.selected.size > 0) { - if (templateProviders(t).length === types.selected.size) { + if (types.selected?.length > 0) { + if (templateProviders(t).length === types.selected.length) { providerFilter = true; } else if (obj.template) { const type = getTemplateProviderType(obj.template); - providerFilter = types.selected.has(type); + providerFilter = types.selected.includes(type); } else { - providerFilter = types.selected.has('user'); + providerFilter = types.selected.includes('user'); } } return providerFilter; diff --git a/frontend/packages/kubevirt-plugin/src/components/vms/table-filters.ts b/frontend/packages/kubevirt-plugin/src/components/vms/table-filters.ts index 071b399b4c5..2360a2c1165 100644 --- a/frontend/packages/kubevirt-plugin/src/components/vms/table-filters.ts +++ b/frontend/packages/kubevirt-plugin/src/components/vms/table-filters.ts @@ -17,8 +17,8 @@ export const vmStatusFilter: RowFilter = { const status = ((obj?.metadata as any) ?.vmStatusBundle as VMStatusBundle)?.status.getSimpleLabel(); return ( - statuses.selected.size === 0 || - statuses.selected.has(status) || + statuses.selected?.length === 0 || + statuses.selected?.includes(status) || !_.includes(statuses.all, status) ); }, @@ -35,9 +35,9 @@ export const vmStatusFilterNew: RowFilter = { filter: (statuses, obj) => { const status = obj?.metadata?.status; return ( - statuses.selected.size === 0 || - statuses.selected.has(status) || - !_.includes(statuses.all, status) + statuses.selected?.length === 0 || + statuses.selected?.includes(status) || + !_.includes(statuses?.all, status) ); }, }; diff --git a/frontend/packages/kubevirt-plugin/src/components/vms/types.ts b/frontend/packages/kubevirt-plugin/src/components/vms/types.ts index 2ea5bd7d5f7..002a3189c57 100644 --- a/frontend/packages/kubevirt-plugin/src/components/vms/types.ts +++ b/frontend/packages/kubevirt-plugin/src/components/vms/types.ts @@ -4,6 +4,8 @@ import { PersistentVolumeClaimKind, PodKind, } from '@console/internal/module/k8s'; +import { V2VVMImportStatus } from '../../constants/v2v-import/ovirt/v2v-vm-import-status'; +import { VMStatusBundle } from '../../statuses/vm/types'; import { V1alpha1DataVolume } from '../../types/api'; import { VMIKind, VMKind } from '../../types/vm'; import { VMImportKind } from '../../types/vm-import/ovirt/vm-import'; @@ -71,3 +73,23 @@ export enum VMTabEnum { nics = 'Network Interfaces', disks = 'Disks', } + +export type ObjectBundle = { + vm: VMKind; + vmi: VMIKind; + vmImport: VMImportKind; +}; + +export type VMRowObjType = { + metadata: { + name: string; + namespace: string; + status: string; + node: string; + creationTimestamp: string; + uid: string; + lookupID: string; + vmStatusBundle: VMStatusBundle; + vmImportStatus?: V2VVMImportStatus; + }; +} & ObjectBundle; diff --git a/frontend/packages/local-storage-operator-plugin/src/components/disks-list/disks-list-page.tsx b/frontend/packages/local-storage-operator-plugin/src/components/disks-list/disks-list-page.tsx index b7dbb2dce26..e3d8cbed4bd 100644 --- a/frontend/packages/local-storage-operator-plugin/src/components/disks-list/disks-list-page.tsx +++ b/frontend/packages/local-storage-operator-plugin/src/components/disks-list/disks-list-page.tsx @@ -175,11 +175,11 @@ export const NodesDisksListPage: React.FC = ({ ); - const diskFilters: RowFilter[] = [ + const diskFilters: RowFilter[] = [ { type: 'disk-state', filterGroupName: t('lso-plugin~Disk State'), - reducer: (disk: DiskMetadata) => { + reducer: (disk) => { return disk?.status?.state; }, items: [ @@ -187,15 +187,12 @@ export const NodesDisksListPage: React.FC = ({ { id: DiskStates.NotAvailable, title: t('lso-plugin~NotAvailable') }, { id: DiskStates.Unknown, title: t('lso-plugin~Unknown') }, ], - filter: ( - states: { all: (keyof typeof DiskStates)[]; selected: Set }, - disk: DiskMetadata, - ) => { + filter: (states, disk) => { if (!states || !states.selected || _.isEmpty(states.selected)) { return true; } const diskState = disk?.status.state; - return states.selected.has(diskState) || !_.includes(states.all, diskState); + return states.selected.includes(diskState) || !_.includes(states.all, diskState); }, }, ]; diff --git a/frontend/packages/metal3-plugin/src/components/baremetal-hosts/table-filters.ts b/frontend/packages/metal3-plugin/src/components/baremetal-hosts/table-filters.ts index 956aa306d8a..7b021d53696 100644 --- a/frontend/packages/metal3-plugin/src/components/baremetal-hosts/table-filters.ts +++ b/frontend/packages/metal3-plugin/src/components/baremetal-hosts/table-filters.ts @@ -64,15 +64,17 @@ export const getHostFilterStatus = (bundle: BareMetalHostBundle): string => { return _.findKey(hostStatesToFilterMap, ({ states }) => states.includes(bundle.status.status)); }; -export const hostStatusFilter = (t: TFunction): RowFilter => ({ +export const hostStatusFilter = (t: TFunction): RowFilter => ({ filterGroupName: 'Status', type: 'host-status', reducer: getHostFilterStatus, items: _.map(hostStatesToFilterMap, ({ title }, id) => ({ id, title: t(title) })), - filter: (groups, bundle: BareMetalHostBundle) => { + filter: (groups, bundle) => { const status = getHostFilterStatus(bundle); return ( - groups.selected.has(status) || !_.includes(groups.all, status) || _.isEmpty(groups.selected) + groups.selected?.includes(status) || + !_.includes(groups.all, status) || + _.isEmpty(groups.selected) ); }, }); diff --git a/frontend/packages/metal3-plugin/src/components/baremetal-nodes/table-filters.ts b/frontend/packages/metal3-plugin/src/components/baremetal-nodes/table-filters.ts index 1423ac1f3df..b630be1922e 100644 --- a/frontend/packages/metal3-plugin/src/components/baremetal-nodes/table-filters.ts +++ b/frontend/packages/metal3-plugin/src/components/baremetal-nodes/table-filters.ts @@ -37,10 +37,12 @@ export const bareMetalNodeStatusFilter = (t: TFunction): RowFilter ({ id, title: t(titleKey) })), - filter: (groups, bundle: BareMetalNodeListBundle) => { + filter: (groups, bundle) => { const status = isCSRBundle(bundle) ? 'approval' : getBareMetalNodeFilterStatus(bundle); return ( - groups.selected.has(status) || !_.includes(groups.all, status) || _.isEmpty(groups.selected) + groups.selected?.includes(status) || + !_.includes(groups.all, status) || + _.isEmpty(groups.selected) ); }, }); diff --git a/frontend/packages/pipelines-plugin/src/utils/pipeline-filter-reducer.ts b/frontend/packages/pipelines-plugin/src/utils/pipeline-filter-reducer.ts index 4c13628de0c..cef1b503cc4 100644 --- a/frontend/packages/pipelines-plugin/src/utils/pipeline-filter-reducer.ts +++ b/frontend/packages/pipelines-plugin/src/utils/pipeline-filter-reducer.ts @@ -65,20 +65,20 @@ export const pipelineRunFilterReducer = (pipelineRun): string => { }; export const pipelineStatusFilter = (filters, pipeline) => { - if (!filters || !filters.selected || !filters.selected.size) { + if (!filters || !filters.selected || !filters.selected.length) { return true; } const status = pipelineFilterReducer(pipeline); - return filters.selected.has(status) || !_.includes(filters.all, status); + return filters.selected?.includes(status) || !_.includes(filters.all, status); }; export const pipelineRunStatusFilter = (phases, pipeline) => { - if (!phases || !phases.selected || !phases.selected.size) { + if (!phases || !phases.selected || !phases.selected.length) { return true; } const status = pipelineRunFilterReducer(pipeline); - return phases.selected.has(status) || !_.includes(phases.all, status); + return phases.selected?.includes(status) || !_.includes(phases.all, status); }; export const pipelineResourceFilterReducer = (pipelineResource): string => { @@ -86,11 +86,11 @@ export const pipelineResourceFilterReducer = (pipelineResource): string => { }; export const pipelineResourceTypeFilter = (filters, pipelineResource): boolean => { - if (!filters || !filters.selected || !filters.selected.size) { + if (!filters || !filters.selected || !filters.selected.length) { return true; } const type = pipelineResourceFilterReducer(pipelineResource); - return filters.selected.has(type) || !_.includes(filters.all, type); + return filters.selected?.includes(type) || !_.includes(filters.all, type); }; export const taskRunFilterReducer = (taskRun): string => { diff --git a/frontend/public/actions/k8s.ts b/frontend/public/actions/k8s.ts index 6775982af34..d39b3416f23 100644 --- a/frontend/public/actions/k8s.ts +++ b/frontend/public/actions/k8s.ts @@ -12,6 +12,7 @@ import { makeReduxID } from '../components/utils/k8s-watcher'; import { APIServiceModel } from '../models'; import { coFetchJSON } from '../co-fetch'; import { referenceForModel, K8sResourceKind, K8sKind, fetchSwagger } from '../module/k8s'; +import { FilterValue } from '../components/factory/table-filters'; export enum ActionType { ReceivedResources = 'resources', @@ -71,7 +72,7 @@ export const getResources = () => (dispatch: Dispatch) => { }); }; -export const filterList = (id: string, name: string, value: string) => +export const filterList = (id: string, name: string, value: FilterValue) => action(ActionType.FilterList, { id, name, value }); export const startWatchK8sObject = (id: string) => action(ActionType.StartWatchK8sObject, { id }); diff --git a/frontend/public/components/RBAC/bindings.jsx b/frontend/public/components/RBAC/bindings.jsx index a261896ce06..aba3eff7ce5 100644 --- a/frontend/public/components/RBAC/bindings.jsx +++ b/frontend/public/components/RBAC/bindings.jsx @@ -10,7 +10,12 @@ import { useActiveNamespace } from '@console/shared'; import { ClusterRoleBindingModel } from '../../models'; import { getQN, k8sCreate, k8sPatch, referenceFor } from '../../module/k8s'; import * as UIActions from '../../actions/ui'; -import { MultiListPage, Table, TableData } from '../factory'; +import { Table, TableData } from '../factory'; +import ListPageFilter from '../factory/ListPage/ListPageFilter'; +import ListPageHeader from '../factory/ListPage/ListPageHeader'; +import ListPageBody from '../factory/ListPage/ListPageBody'; +import { useListPageFilter } from '../factory/ListPage/filter-hook'; +import { ListPageCreateLink } from '../factory/ListPage/ListPageCreate'; import { RadioGroup } from '../radio'; import { confirmModal } from '../modals'; import { @@ -34,6 +39,7 @@ import { connectToFlags } from '../../reducers/connectToFlags'; import { flagPending } from '../../reducers/features'; import { useTranslation, withTranslation } from 'react-i18next'; import i18next from 'i18next'; +import { useK8sWatchResources } from '../utils/k8s-watch-hook'; const bindingKind = (binding) => binding.metadata.namespace ? 'RoleBinding' : 'ClusterRoleBinding'; @@ -280,63 +286,101 @@ export const bindingType = (binding) => { return binding.metadata.namespace ? 'namespace' : 'cluster'; }; -const roleResources = [ - { kind: 'RoleBinding', namespaced: true }, - { kind: 'ClusterRoleBinding', namespaced: false, optional: true }, -]; - export const RoleBindingsPage = ({ namespace = undefined, showTitle = true, mock = false, - staticFilters = undefined, + staticFilters, name, kind, createPath = `/k8s/cluster/rolebindings/~new${ name && kind ? `?subjectName=${name}&subjectKind=${kind}` : '' }`, + hideLabelFilter = false, + hideNameLabelFilters = false, + hideColumnManagement = false, }) => { const { t } = useTranslation(); - const rowFilters = [ - { - filterGroupName: t('public~Kind'), - type: 'role-binding-kind', - reducer: bindingType, - itemsGenerator: ({ ClusterRoleBinding: data }) => { - const items = [ - { id: 'namespace', title: t('public~Namespace RoleBindings') }, - { id: 'system', title: t('public~System RoleBindings') }, - ]; - if (data && data.loaded && !data.loadError) { - items.unshift({ - id: 'cluster', - title: t('public~Cluster-wide RoleBindings'), - }); - } - return items; - }, + const resources = useK8sWatchResources({ + RoleBinding: { + kind: 'RoleBinding', + namespaced: true, + namespace, + isList: true, }, - ]; + ClusterRoleBinding: { + kind: 'ClusterRoleBinding', + namespaced: false, + isList: true, + }, + }); + + const data = React.useMemo(() => flatten(resources), [resources]); + + const loaded = Object.values(resources).every((r) => r.loaded); + + const hasCRBindings = + resources.ClusterRoleBinding.data?.length > 0 && + resources.ClusterRoleBinding.loaded && + !resources.ClusterRoleBinding.loadError; + + const rowFilters = React.useMemo( + () => [ + { + filterGroupName: t('public~Kind'), + type: 'role-binding-kind', + reducer: bindingType, + filter: (filter, binding) => + filter.selected?.includes(bindingType(binding)) || !filter.selected?.size, + items: hasCRBindings + ? [ + { + id: 'cluster', + title: t('public~Cluster-wide RoleBindings'), + }, + { id: 'namespace', title: t('public~Namespace RoleBindings') }, + { id: 'system', title: t('public~System RoleBindings') }, + ] + : [ + { id: 'namespace', title: t('public~Namespace RoleBindings') }, + { id: 'system', title: t('public~System RoleBindings') }, + ], + }, + ], + [hasCRBindings, t], + ); + + const [staticData, filteredData, onFilterChange] = useListPageFilter( + data, + rowFilters, + staticFilters, + ); + return ( - + <> + + {!mock && ( + {t('public~Create binding')} + )} + + + + + + ); }; diff --git a/frontend/public/components/cron-job.tsx b/frontend/public/components/cron-job.tsx index 3d892fb3951..5f52f40e8e7 100644 --- a/frontend/public/components/cron-job.tsx +++ b/frontend/public/components/cron-job.tsx @@ -183,37 +183,41 @@ const getPodsWatcher = (namespace: string) => { ]; }; -export const CronJobPodsComponent: React.FC = ({ obj }) => ( -
- - , - ) => { - if (!_resources.jobs.loaded || !_resources.pods.loaded) { - return []; - } - const jobs = _resources.jobs.data.filter((job) => - job.metadata?.ownerReferences?.find((ref) => ref.uid === obj.metadata.uid), - ); - return ( - jobs && - jobs.reduce((acc, job) => { - acc.push(...getPodsForResource(job, _resources)); - return acc; - }, []) - ); - }} - kinds={['Pods']} - ListComponent={PodList} - rowFilters={getPodFilters()} - /> - -
-); +export const CronJobPodsComponent: React.FC = ({ obj }) => { + const { t } = useTranslation(); + const podFilters = React.useMemo(() => getPodFilters(t), [t]); + return ( +
+ + , + ) => { + if (!_resources.jobs.loaded || !_resources.pods.loaded) { + return []; + } + const jobs = _resources.jobs.data.filter((job) => + job.metadata?.ownerReferences?.find((ref) => ref.uid === obj.metadata.uid), + ); + return ( + jobs && + jobs.reduce((acc, job) => { + acc.push(...getPodsForResource(job, _resources)); + return acc; + }, []) + ); + }} + kinds={['Pods']} + ListComponent={PodList} + rowFilters={podFilters} + /> + +
+ ); +}; export type CronJobJobsComponentProps = { obj: K8sResourceKind; diff --git a/frontend/public/components/daemon-set.tsx b/frontend/public/components/daemon-set.tsx index 24b4de89f99..c494755bc63 100644 --- a/frontend/public/components/daemon-set.tsx +++ b/frontend/public/components/daemon-set.tsx @@ -231,7 +231,7 @@ export const DaemonSetsPage: React.FC = (props) => ( ); const DaemonSetPods: React.FC = (props) => ( - + ); export const DaemonSetsDetailsPage: React.FC = (props) => { diff --git a/frontend/public/components/factory/ListPage/ListPageBody.tsx b/frontend/public/components/factory/ListPage/ListPageBody.tsx new file mode 100644 index 00000000000..df3d21644fd --- /dev/null +++ b/frontend/public/components/factory/ListPage/ListPageBody.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const ListPageBody: React.FC = ({ children }) => { + return
{children}
; +}; + +export default ListPageBody; diff --git a/frontend/public/components/factory/ListPage/ListPageCreate.tsx b/frontend/public/components/factory/ListPage/ListPageCreate.tsx new file mode 100644 index 00000000000..ebed207382d --- /dev/null +++ b/frontend/public/components/factory/ListPage/ListPageCreate.tsx @@ -0,0 +1,141 @@ +import * as React from 'react'; +import * as _ from 'lodash'; +import { Link } from 'react-router-dom'; +import { + Button, + ButtonProps, + DropdownToggle, + Dropdown, + DropdownItem, +} from '@patternfly/react-core'; +import { CaretDownIcon } from '@patternfly/react-icons'; +import { useK8sModel } from '@console/shared/src/hooks/useK8sModel'; +import { useActiveNamespace } from '@console/shared/src/hooks/useActiveNamespace'; +import { ALL_NAMESPACES_KEY } from '@console/shared/src/constants/common'; + +import { RequireCreatePermission } from '../../utils'; +import { K8sKind, GroupVersionKind } from '../../../module/k8s/types'; + +type CreateWithPermissionsProps = { + createAccessReview?: { + model: K8sKind; + namespace?: string; + }; +}; + +const CreateWithPermissions: React.FC = ({ + createAccessReview, + children, +}) => + !_.isEmpty(createAccessReview) ? ( + + {children} + + ) : ( + <>{children} + ); + +type ListPageCreateLinkProps = CreateWithPermissionsProps & { + to: string; +}; + +export const ListPageCreateLink: React.FC = ({ + to, + createAccessReview, + children, +}) => ( + + + + + +); + +type ListPageCreateButtonProps = CreateWithPermissionsProps & ButtonProps; + +export const ListPageCreateButton: React.FC = ({ + createAccessReview, + ...rest +}) => ( + +