Skip to content

Commit

Permalink
feat(THEEDGE-3581): add tab controls to display detail groups (#2079)
Browse files Browse the repository at this point in the history
* feat(THEEDGE-3581): add tab controls to display detail groups;

* feat(THEEDGE-3581): use new component; enable remove frm group and update action;

* feat(THEEDGE-3581): add deviceGroupView

* feat(THEEDGE-3581): add feature flag validation

* feat(THEEDGE-3581): fix condition to display tabs when not edge enabled

* feat(THEEDGE-3581): clean up comments and log

* feat(THEEDGE-3581): clean up comments and log

* feat(THEEDGE-3581): change behavior on catch
  • Loading branch information
acosferreira authored Nov 3, 2023
1 parent acfffd4 commit 0c8907f
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 5 deletions.
101 changes: 101 additions & 0 deletions src/components/InventoryGroupDetail/GroupTabDetails.js
Original file line number Diff line number Diff line change
@@ -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 (
<Tabs
activeKey={activeTabKey}
onSelect={(event, value) => setActiveTabKey(value)}
aria-label="Group tabs"
role="region"
inset={{ default: 'insetMd' }} // add extra space before the first tab (according to mocks)
mountOnEnter
unmountOnExit
>
<Tab eventKey={0} title="Systems" aria-label="Group systems tab">
<PageSection>
{canViewHosts && hasEdgeImages ? (
<Tabs
className="pf-m-light pf-c-table"
activeKey={activeTab && tab == 0 ? activeTab : tab}
onSelect={handleTabClick}
aria-label="Hybrid inventory tabs"
>
<Tab
eventKey={hybridInventoryTabKeys.conventional.key}
title={<TabTitleText>Conventional (RPM-DNF)</TabTitleText>}
>
<GroupSystems groupName={groupName} groupId={groupId} />
</Tab>
<Tab
eventKey={hybridInventoryTabKeys.immutable.key}
title={<TabTitleText>Immutable (OSTree)</TabTitleText>}
>
<EdgeDeviceGroupiew groupUUID={groupId} isSystemsView={true} />
</Tab>
</Tabs>
) : canViewHosts ? (
<GroupSystems groupName={groupName} groupId={groupId} />
) : (
<EmptyStateNoAccessToSystems />
)}
</PageSection>
</Tab>
<Tab eventKey={1} title="Group info" aria-label="Group info tab">
{activeTabKey === 1 && ( // helps to lazy load the component
<PageSection>
<Suspense
fallback={
<Bullseye>
<Spinner />
</Bullseye>
}
>
<GroupDetailInfo />
</Suspense>
</PageSection>
)}
</Tab>
</Tabs>
);
};

GroupTabDetailsWrapper.propTypes = {
groupName: PropTypes.string.isRequired,
groupId: PropTypes.string.isRequired,
activeTab: PropTypes.string,
hasEdgeImages: PropTypes.bool,
};
export default GroupTabDetailsWrapper;
88 changes: 83 additions & 5 deletions src/components/InventoryGroupDetail/InventoryGroupDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 }) => (
<Suspense
fallback={
<Bullseye>
<Spinner size="xl" />
</Bullseye>
}
>
{children}
</Suspense>
);

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();
Expand All @@ -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)
);
Expand All @@ -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 ? (
<React.Fragment>
<GroupDetailHeader groupId={groupId} />
{canViewGroup ? (
<PageSection variant="light" type="tabs">
<GroupTabDetails
groupId={groupId}
groupName={groupName}
activeTab={activeTab}
hasEdgeImages={hasEdgeImages}
/>
</PageSection>
) : (
<PageSection>
<EmptyStateNoAccessToGroup />
</PageSection>
)}
</React.Fragment>
) : (
<React.Fragment>
<GroupDetailHeader groupId={groupId} />
{canViewGroup ? (
Expand Down Expand Up @@ -104,6 +177,11 @@ const InventoryGroupDetail = ({ groupId }) => {

InventoryGroupDetail.propTypes = {
groupId: PropTypes.string.isRequired,
groupName: PropTypes.string,
};

SuspenseWrapper.propTypes = {
children: PropTypes.element,
};

export default InventoryGroupDetail;
Original file line number Diff line number Diff line change
@@ -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 (
<AsyncComponent
appName="edge"
module="./DevicesGroupDetail"
ErrorComponent={<ErrorState />}
navigateProp={useNavigate}
locationProp={useLocation}
showHeaderProp={false}
pathPrefix={resolveRelPath('')}
urlName={manageEdgeInventoryUrlName}
notificationProp={notificationProp}
{...props}
/>
);
};

export default EdgeDeviceGroupiew;

0 comments on commit 0c8907f

Please sign in to comment.