diff --git a/src/components/InventoryGroupDetail/GroupTabDetails.js b/src/components/InventoryGroupDetail/GroupTabDetails.js new file mode 100644 index 000000000..149f934ab --- /dev/null +++ b/src/components/InventoryGroupDetail/GroupTabDetails.js @@ -0,0 +1,101 @@ +import { + Bullseye, + PageSection, + Spinner, + Tab, + TabTitleText, + Tabs, +} from '@patternfly/react-core'; +import React, { Suspense, lazy, useState } from 'react'; +import { hybridInventoryTabKeys } from '../../Utilities/constants'; +import GroupSystems from '../GroupSystems'; +import PropTypes from 'prop-types'; +import { usePermissionsWithContext } from '@redhat-cloud-services/frontend-components-utilities/RBACHook'; +import { REQUIRED_PERMISSIONS_TO_READ_GROUP_HOSTS } from '../../constants'; +import EdgeDeviceGroupiew from '../InventoryTabs/ImmutableDevices/EdgeDevicesGroupView'; +import { EmptyStateNoAccessToSystems } from './EmptyStateNoAccess'; + +const GroupDetailInfo = lazy(() => import('./GroupDetailInfo')); + +const GroupTabDetailsWrapper = ({ + groupId, + groupName, + activeTab, + hasEdgeImages, +}) => { + const [tab, setTab] = useState(0); + const { hasAccess: canViewHosts } = usePermissionsWithContext( + REQUIRED_PERMISSIONS_TO_READ_GROUP_HOSTS(groupId) + ); + + const handleTabClick = (_event, tabIndex) => { + setTab(tabIndex); + }; + + const [activeTabKey, setActiveTabKey] = useState(0); + + return ( + setActiveTabKey(value)} + aria-label="Group tabs" + role="region" + inset={{ default: 'insetMd' }} // add extra space before the first tab (according to mocks) + mountOnEnter + unmountOnExit + > + + + {canViewHosts && hasEdgeImages ? ( + + Conventional (RPM-DNF)} + > + + + Immutable (OSTree)} + > + + + + ) : canViewHosts ? ( + + ) : ( + + )} + + + + {activeTabKey === 1 && ( // helps to lazy load the component + + + + + } + > + + + + )} + + + ); +}; + +GroupTabDetailsWrapper.propTypes = { + groupName: PropTypes.string.isRequired, + groupId: PropTypes.string.isRequired, + activeTab: PropTypes.string, + hasEdgeImages: PropTypes.bool, +}; +export default GroupTabDetailsWrapper; diff --git a/src/components/InventoryGroupDetail/InventoryGroupDetail.js b/src/components/InventoryGroupDetail/InventoryGroupDetail.js index 34015fd3a..09bd4487d 100644 --- a/src/components/InventoryGroupDetail/InventoryGroupDetail.js +++ b/src/components/InventoryGroupDetail/InventoryGroupDetail.js @@ -11,6 +11,7 @@ import React, { Suspense, lazy, useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { fetchGroupDetail } from '../../store/inventory-actions'; import GroupSystems from '../GroupSystems'; +import GroupTabDetails from './GroupTabDetails'; import GroupDetailHeader from './GroupDetailHeader'; import { usePermissionsWithContext } from '@redhat-cloud-services/frontend-components-utilities/RBACHook'; import { @@ -21,10 +22,35 @@ import { EmptyStateNoAccessToGroup, EmptyStateNoAccessToSystems, } from './EmptyStateNoAccess'; +import useFeatureFlag from '../../Utilities/useFeatureFlag'; +import axios from 'axios'; +import { + INVENTORY_TOTAL_FETCH_CONVENTIONAL_PARAMS, + INVENTORY_TOTAL_FETCH_EDGE_PARAMS, + INVENTORY_TOTAL_FETCH_URL_SERVER, + hybridInventoryTabKeys, +} from '../../Utilities/constants'; -const GroupDetailInfo = lazy(() => import('./GroupDetailInfo')); +const SuspenseWrapper = ({ children }) => ( + + + + } + > + {children} + +); +const GroupDetailInfo = lazy(() => import('./GroupDetailInfo')); const InventoryGroupDetail = ({ groupId }) => { + const [activeTabKey, setActiveTabKey] = useState(0); + + const [activeTab, setActiveTab] = useState( + hybridInventoryTabKeys.conventional.key + ); + const dispatch = useDispatch(); const { data } = useSelector((state) => state.groupDetail); const chrome = useChrome(); @@ -33,7 +59,6 @@ const InventoryGroupDetail = ({ groupId }) => { const { hasAccess: canViewGroup } = usePermissionsWithContext( REQUIRED_PERMISSIONS_TO_READ_GROUP(groupId) ); - const { hasAccess: canViewHosts } = usePermissionsWithContext( REQUIRED_PERMISSIONS_TO_READ_GROUP_HOSTS(groupId) ); @@ -51,11 +76,59 @@ const InventoryGroupDetail = ({ groupId }) => { ); }, [data]); - const [activeTabKey, setActiveTabKey] = useState(0); - // TODO: append search parameter to identify the active tab - return ( + const [hasEdgeImages, setHasEdgeImages] = useState(false); + const EdgeParityEnabled = useFeatureFlag('edgeParity.inventory-list'); + useEffect(() => { + if (EdgeParityEnabled) { + try { + axios + .get( + `${INVENTORY_TOTAL_FETCH_URL_SERVER}${INVENTORY_TOTAL_FETCH_EDGE_PARAMS}&group_name=${groupName}` + ) + .then((result) => { + const accountHasEdgeImages = result?.data?.total > 0; + setHasEdgeImages(accountHasEdgeImages); + axios + .get( + `${INVENTORY_TOTAL_FETCH_URL_SERVER}${INVENTORY_TOTAL_FETCH_CONVENTIONAL_PARAMS}&group_name=${groupName}` + ) + .then((conventionalImages) => { + const accountHasConventionalImages = + conventionalImages?.data?.total > 0; + if (accountHasEdgeImages && !accountHasConventionalImages) { + setActiveTab(hybridInventoryTabKeys.immutable.key); + } else { + setActiveTab(hybridInventoryTabKeys.conventional.key); + } + }); + }); + } catch (e) { + setHasEdgeImages(false); + setActiveTab(hybridInventoryTabKeys.conventional.key); + } + } + }, [data]); + return hasEdgeImages && canViewGroup && EdgeParityEnabled ? ( + + + {canViewGroup ? ( + + + + ) : ( + + + + )} + + ) : ( {canViewGroup ? ( @@ -104,6 +177,11 @@ const InventoryGroupDetail = ({ groupId }) => { InventoryGroupDetail.propTypes = { groupId: PropTypes.string.isRequired, + groupName: PropTypes.string, +}; + +SuspenseWrapper.propTypes = { + children: PropTypes.element, }; export default InventoryGroupDetail; diff --git a/src/components/InventoryTabs/ImmutableDevices/EdgeDevicesGroupView.js b/src/components/InventoryTabs/ImmutableDevices/EdgeDevicesGroupView.js new file mode 100644 index 000000000..0bb8b6ac2 --- /dev/null +++ b/src/components/InventoryTabs/ImmutableDevices/EdgeDevicesGroupView.js @@ -0,0 +1,31 @@ +import React from 'react'; +import AsyncComponent from '@redhat-cloud-services/frontend-components/AsyncComponent'; +import ErrorState from '@redhat-cloud-services/frontend-components/ErrorState'; +import { resolveRelPath } from '../../../Utilities/path'; +import { + getNotificationProp, + manageEdgeInventoryUrlName, +} from '../../../Utilities/edge'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { useDispatch } from 'react-redux'; + +const EdgeDeviceGroupiew = (props) => { + const dispatch = useDispatch(); + const notificationProp = getNotificationProp(dispatch); + return ( + } + navigateProp={useNavigate} + locationProp={useLocation} + showHeaderProp={false} + pathPrefix={resolveRelPath('')} + urlName={manageEdgeInventoryUrlName} + notificationProp={notificationProp} + {...props} + /> + ); +}; + +export default EdgeDeviceGroupiew;