Katello/katello

View on GitHub
webpack/components/extensions/Hosts/BulkActions/BulkPackagesWizard/BulkPackagesWizard.js

Summary

Maintainability
F
3 days
Test Coverage
import React, { useState, createContext, useContext } from 'react';
import { Radio, Text, TextVariants, TextContent, Alert } from '@patternfly/react-core';
import { Wizard, WizardHeader, WizardStep } from '@patternfly/react-core/next';
import { translate as __ } from 'foremanReact/common/I18n';
import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
import { useBulkSelect } from 'foremanReact/components/PF4/TableIndexPage/Table/TableHooks';
import { ForemanActionsBarContext } from 'foremanReact/components/HostDetails/ActionsBar';
import { useTableIndexAPIResponse } from 'foremanReact/components/PF4/TableIndexPage/Table/TableIndexHooks';
import { STATUS } from 'foremanReact/constants';
import { HOSTS_API_PATH } from 'foremanReact/routes/Hosts/constants';
import HostReview from '../HostReview';
import { BulkPackagesReview, dropdownOptions } from './04_Review';
import { BulkPackagesUpgradeTable, BulkPackagesInstallTable, BulkPackagesRemoveTable } from './02_BulkPackagesTable';
import { BulkPackagesReviewFooter } from './04_ReviewFooter';
import katelloApi, { foremanApi } from '../../../../../services/api';

export const UPGRADE_ALL = 'upgradeAll';
export const UPGRADE = 'upgrade';
export const INSTALL = 'install';
export const REMOVE = 'remove';

export const BulkPackagesWizardContext = createContext({});

export const useHostsBulkSelect = ({ initialSelectedHosts, modalIsOpen }) => {
  const defaultParams = { search: initialSelectedHosts };
  const apiOptions = { key: 'HOST_REVIEW' };
  const replacementResponse = !modalIsOpen ? { response: {} } : false;
  const hostsResponse = useTableIndexAPIResponse({
    replacementResponse, // don't fetch data if modal is closed
    apiUrl: `${HOSTS_API_PATH}?per_page=7`,
    apiOptions,
    defaultParams,
  });

  const {
    response: {
      results: hostsResults,
      ...hostsMetadata
    },
  } = hostsResponse;

  const { total, page, subtotal } = hostsMetadata;

  return {
    hostsBulkSelect: useBulkSelect({
      results: hostsResults,
      metadata: { total, page, selectable: subtotal },
      initialSearchQuery: initialSelectedHosts,
      initialSelectAllMode: true,
    }),
    hostsResponse,
    hostsMetadata,
  };
};

export const getPackagesUrl = (selectedAction) => {
  if (selectedAction === REMOVE) {
    return `${foremanApi.getApiUrl('/hosts/host_packages/installed_packages')}?per_page=7&include_permissions=true`;
  }

  return `${katelloApi.getApiUrl('/packages/thindex')}?per_page=7&include_permissions=true&packages_restrict_upgradable=${selectedAction === 'upgrade'}`;
};

const BulkPackagesWizard = () => {
  const { modalOpen, setModalClosed: closeModal } = useForemanModal({ id: 'bulk-packages-wizard' });


  const [selectedAction, setSelectedAction] = useState(UPGRADE_ALL);

  const { selectedCount: initialSelectedHostCount, fetchBulkParams }
    = useContext(ForemanActionsBarContext);

  const [shouldValidateStep2, setShouldValidateStep2] = useState(false);
  const [shouldValidateStep3, setShouldValidateStep3] = useState(false);
  const [finishButtonLoading, setFinishButtonLoading] = useState(false);
  const [selectedRexOption, setSelectedRexOption] = useState(dropdownOptions[0]);
  const packageActionsNames = {
    install: __('Install packages'), remove: __('Remove packages'), upgrade: __('Upgrade packages'), upgradeAll: __('Upgrade packages'),
  };
  const packageActions = () => {
    switch (selectedAction) {
    case INSTALL:
      return (
        <BulkPackagesInstallTable modalIsOpen={modalOpen} />
      );
    case REMOVE:
      return (
        <BulkPackagesRemoveTable modalIsOpen={modalOpen} />
      );
    default:
      return (
        <BulkPackagesUpgradeTable modalIsOpen={modalOpen} />
      );
    }
  };

  const finishButtonTextValues = {
    install: __('Install'), remove: __('Remove'), upgrade: __('Upgrade'), upgradeAll: __('Upgrade'),
  };
  const finishButtonText = finishButtonTextValues[selectedAction];
  const PACKAGES_URL = getPackagesUrl(selectedAction);
  const apiOptions = { key: 'BULK_HOST_PACKAGES' };
  const replacementResponse = !modalOpen ? { response: {} } : false;
  const packagesResponse = useTableIndexAPIResponse({
    replacementResponse, // don't fetch data if modal is closed
    apiUrl: PACKAGES_URL,
    apiOptions,
  });

  const {
    response: {
      results: packagesResults,
      ...packagesMetadata
    },
    status: packagesStatus,
  } = packagesResponse;

  const { total, page, subtotal } = packagesMetadata;

  const packagesBulkSelect = useBulkSelect({
    results: packagesResults,
    metadata: { total, page, selectable: subtotal },
    idColumn: 'name',
  });

  const initialSelectedHosts = fetchBulkParams();

  const hostsBulkSelect =
    useHostsBulkSelect({ initialSelectedHosts, modalIsOpen: modalOpen });

  // eslint-disable-next-line no-restricted-globals
  const selectionIsValid = count => count > 0 || isNaN(count);
  const packagesResultsPresent = packagesResults?.length > 0;
  const packageSelectionIsValid =
    selectionIsValid(packagesBulkSelect.selectedCount) || selectedAction === UPGRADE_ALL;
  const hostSelectionIsValid = selectionIsValid(hostsBulkSelect.hostsBulkSelect.selectedCount);
  let step2Valid = shouldValidateStep2 ? packageSelectionIsValid : true;
  if (!packagesResultsPresent) step2Valid = false;
  const step3Valid = shouldValidateStep3 ? hostSelectionIsValid : true;
  const step4Valid = hostSelectionIsValid && packageSelectionIsValid;

  const bulkPackagesWizardContextData = {
    selectedAction,
    finishButtonText,
    initialSelectedHostCount,
    setShouldValidateStep2,
    finishButtonLoading,
    setFinishButtonLoading,
    selectedRexOption,
    setSelectedRexOption,
    closeModal,
    packagesBulkSelect,
    packagesResults,
    packagesMetadata,
    packagesResponse,
    hostsBulkSelect: hostsBulkSelect.hostsBulkSelect,
  };
  return (
    <BulkPackagesWizardContext.Provider value={bulkPackagesWizardContextData}>
      <Wizard
        header={<WizardHeader title={__('Manage packages')} onClose={closeModal} />}
      >
        <WizardStep
          name={__('Select action')}
          id="mpw-step-1"
          footer={{ onClose: closeModal }}
        >
          <TextContent>
            <Text ouiaId="mpw-step-1-header" component={TextVariants.h2}>
              {__('Select action')}
            </Text>
            <Text ouiaId="mpw-step-1-content" component={TextVariants.p}>
              {__('To manage packages, select an action.')}
            </Text>
          </TextContent>
          {packagesStatus === STATUS.RESOLVED && !packagesResultsPresent && (
            <Alert
              ouiaId="no-packages-found-alert"
              variant="info"
              isInline
              title={__('No upgradable packages found.')}
              style={{ marginBottom: '1rem' }}
            />
          )}
          <div style={{ marginBottom: '1rem' }} />
          <div style={{ marginLeft: '1rem' }}>
            <Radio
              isChecked={selectedAction === UPGRADE_ALL}
              name="packageActionRadioGroup"
              onChange={() => setSelectedAction(UPGRADE_ALL)}
              label={__('Upgrade all packages')}
              id="r1-upgrade-all-packages"
              ouiaId="r1-upgrade-all-packages"
            />
            <Radio
              isChecked={selectedAction === UPGRADE}
              name="packageActionRadioGroup"
              onChange={() => setSelectedAction(UPGRADE)}
              label={__('Upgrade packages')}
              id="r2-upgrade-packages"
              ouiaId="r2-upgrade-packages"
            />
            <Radio
              isChecked={selectedAction === INSTALL}
              name="packageActionRadioGroup"
              onChange={() => setSelectedAction(INSTALL)}
              label={__('Install packages')}
              id="r3-install-packages"
              ouiaId="r3-install-packages"
            />
            <Radio
              isChecked={selectedAction === REMOVE}
              name="packageActionRadioGroup"
              onChange={() => setSelectedAction(REMOVE)}
              label={__('Remove packages')}
              id="r4-remove-packages"
              ouiaId="r4-remove-packages"
            />
          </div>
        </WizardStep>
        <WizardStep
          name={packageActionsNames[selectedAction]}
          id="mpw-step-2"
          isHidden={selectedAction === UPGRADE_ALL}
          footer={{ isNextDisabled: !step2Valid, onClose: closeModal }}
          status={step2Valid ? 'default' : 'error'}
        >
          {packageActions()}
        </WizardStep>
        <WizardStep
          name={__('Review hosts')}
          id="mpw-step-3"
          status={step3Valid ? 'default' : 'error'}
          footer={{ isNextDisabled: !step4Valid || !packagesResultsPresent, onClose: closeModal }}
        >
          <HostReview
            key={modalOpen}
            selectedAction={selectedAction}
            hostsBulkSelect={hostsBulkSelect}
            initialSelectedHosts={initialSelectedHosts}
            setShouldValidateStep={setShouldValidateStep3}
          />
        </WizardStep>
        <WizardStep
          name={__('Review')}
          id="mpw-review-step"
          footer={<BulkPackagesReviewFooter />}
          isDisabled={!step4Valid || !packagesResultsPresent}
        >
          <BulkPackagesReview />
        </WizardStep>
      </Wizard>
    </BulkPackagesWizardContext.Provider>
  );
};

export default BulkPackagesWizard;