Source

components/InventoryDetail/ApplicationDetails.js

import React, { Suspense, useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { useSelector, useStore } from 'react-redux';
import { Spinner, Tab, TabContent, Tabs } from '@patternfly/react-core';
import { verifyCulledReporter } from '../../Utilities/sharedFunctions';
import { getFact } from './helpers';
import { NotConnected } from '@redhat-cloud-services/frontend-components/NotConnected';
import {
  APP_NAME_ADVISOR,
  APP_NAME_PATCH,
  APP_NAME_VULNERABILITY,
  REPORTER_PUPTOO,
  REPORTER_RHSM_CONDUIT,
  REPORTER_RHSM_PROFILE_BRIDGE,
} from '../../Utilities/constants';
import { PageSection } from '@patternfly/react-core';

/**
 * Component that renders tabs for each application detail and handles clicking on each item.
 *  @param {*} props onTabSelect can be used to notify parent component that detail has been selected.
 */
const ApplicationDetails = ({
  onTabSelect,
  appList,
  activeApp,
  inventoryId,
  entity,
  ...props
}) => {
  const store = useStore();
  const items = useSelector(({ entityDetails }) => {
    return (entityDetails?.activeApps || appList || [])
      .filter(({ isVisible }) => isVisible !== false)
      .map((app) => ({
        ...app,
        tabRef: React.createRef(),
      }));
  });
  const disabledApps = useSelector(
    ({ systemProfileStore }) => systemProfileStore?.disabledApps,
  );
  const [activeTabs, setActiveTabs] = useState(items);
  const [currentApp, setCurrentApp] = useState(activeApp || items?.[0]?.name);

  const perReporterStaleness = getFact('per_reporter_staleness', entity);

  useEffect(() => {
    const filteredResult = items.filter(
      (app) => !disabledApps?.includes(app.name),
    );
    if (filteredResult !== 0 && typeof filteredResult !== 'undefined') {
      setActiveTabs(filteredResult);
    } else {
      setActiveTabs(items);
    }
    setCurrentApp(activeApp || items?.[0]?.name);
  }, [disabledApps, appList]);

  const isDisconnected = useMemo(
    () => verifyCulledReporter(perReporterStaleness, REPORTER_PUPTOO),
    [currentApp],
  );

  const isRHSMSystem = useMemo(() => {
    const rhsmConduit =
      !!perReporterStaleness?.[REPORTER_RHSM_CONDUIT] &&
      !verifyCulledReporter(perReporterStaleness, REPORTER_RHSM_CONDUIT);
    const rhsmBridge = !!perReporterStaleness?.[REPORTER_RHSM_PROFILE_BRIDGE];
    return rhsmBridge && rhsmConduit;
  }, [currentApp]);

  const isEmptyState = (currentApp) =>
    (currentApp === APP_NAME_ADVISOR && isDisconnected) ||
    (currentApp === APP_NAME_VULNERABILITY &&
      !isRHSMSystem &&
      isDisconnected) ||
    (currentApp === APP_NAME_PATCH && !isRHSMSystem && isDisconnected);

  return (
    <React.Fragment>
      {activeTabs.length > 0 ? (
        <React.Fragment>
          <section className="pf-v5-u-pr-lg pf-v5-u-pl-lg pf-v5-u-background-color-100-on-md">
            <Tabs
              {...props}
              activeKey={currentApp}
              onSelect={(event, item) => {
                const activeItem = activeTabs.find(
                  (oneApp) => oneApp.name === item,
                );
                if (onTabSelect) {
                  onTabSelect(event, item, activeItem.name || item);
                }

                setCurrentApp(activeItem.name);
              }}
              className="ins-c-inventory-detail__app-tabs"
              inset={'insetMd'}
            >
              {activeTabs?.map((item, key) => (
                <Tab
                  key={key}
                  eventKey={item.name}
                  title={item.title}
                  tabContentRef={item.tabRef}
                  {...item}
                />
              ))}
            </Tabs>
          </section>
          <section>
            {activeTabs?.map((item) => {
              const Cmp = item.component;
              return (
                <TabContent
                  eventKey={item.name}
                  id={item.name}
                  ref={item.tabRef}
                  aria-label={item.title}
                  key={item.name}
                >
                  {item.name === currentApp && (
                    <Suspense fallback={Spinner}>
                      <PageSection>
                        {isEmptyState(currentApp) ? (
                          <NotConnected />
                        ) : (
                          <Cmp
                            inventoryId={inventoryId}
                            store={store}
                            {...item}
                          />
                        )}
                      </PageSection>
                    </Suspense>
                  )}
                </TabContent>
              );
            })}
          </section>
        </React.Fragment>
      ) : (
        <Spinner />
      )}
    </React.Fragment>
  );
};

ApplicationDetails.propTypes = {
  appList: PropTypes.arrayOf(
    PropTypes.shape({
      title: PropTypes.node,
      name: PropTypes.string.isRequired,
      pageId: PropTypes.string,
    }),
  ),
  onTabSelect: PropTypes.func,
  activeApp: PropTypes.string,
  inventoryId: PropTypes.string.isRequired,
  entity: PropTypes.object,
};

export default ApplicationDetails;