theforeman/foreman

View on GitHub
webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/index.js

Summary

Maintainability
D
2 days
Test Coverage
/* eslint-disable max-lines */
import React, { useState, useEffect, useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';

import {
  Alert,
  Button,
  Form,
  Grid,
  GridItem,
  Tab,
  Tabs,
  TabContent,
  TabTitleText,
} from '@patternfly/react-core';

import { translate as __ } from '../../../common/I18n';
import { getDocsURL } from '../../../common/helpers';
import {
  useForemanOrganization,
  useForemanLocation,
} from '../../../Root/Context/ForemanContext';
import { STATUS } from '../../../constants';
import PageLayout from '../../common/PageLayout/PageLayout';
import Slot from '../../../components/common/Slot';

import {
  selectAPIStatusData,
  selectAPIStatusCommand,
  selectOrganizations,
  selectLocations,
  selectHostGroups,
  selectCommand,
  selectConfigParams,
  selectOperatingSystems,
  selectOperatingSystemTemplate,
  selectSmartProxies,
  selectPluginData,
} from './RegistrationCommandsPageSelectors';
import { dataAction, commandAction } from './RegistrationCommandsPageActions';

import General from './components/General';
import Advanced from './components/Advanced';
import Actions from './components/Actions';
import Command from './components/Command';
import './RegistrationCommandsPage.scss';

const RegistrationCommandsPage = () => {
  const dispatch = useDispatch();

  // Context
  const currentOrganization = useForemanOrganization();
  const currentLocation = useForemanLocation();

  // Form tabs
  const [activeTab, setActiveTab] = useState(0);
  const generalTabRef = React.createRef();
  const advancedTabRef = React.createRef();

  // API statuses
  const apiStatusCommand = useSelector(selectAPIStatusCommand);
  const apiStatusData = useSelector(selectAPIStatusData);
  const isLoading = apiStatusData === STATUS.PENDING;
  const isGenerating = apiStatusCommand === STATUS.PENDING;

  // Form data
  const organizations = useSelector(selectOrganizations);
  const locations = useSelector(selectLocations);
  const hostGroups = useSelector(selectHostGroups);
  const operatingSystems = useSelector(selectOperatingSystems);
  const operatingSystemTemplate = useSelector(selectOperatingSystemTemplate);
  const smartProxies = useSelector(selectSmartProxies);
  const configParams = useSelector(selectConfigParams);
  const pluginData = useSelector(selectPluginData);

  // Form values
  const [organizationId, setOrganizationId] = useState(currentOrganization?.id);
  const [locationId, setLocationId] = useState(currentLocation?.id);
  const [hostGroupId, setHostGroupId] = useState();
  const [operatingSystemId, setOperatingSystemId] = useState();
  const [smartProxyId, setSmartProxyId] = useState();
  const [insecure, setInsecure] = useState(false);
  const [setupRemoteExecution, setSetupRemoteExecution] = useState('');
  const [setupInsights, setSetupInsights] = useState('');
  const [jwtExpiration, setJwtExpiration] = useState(4);
  const [packages, setPackages] = useState('');
  const [updatePackages, setUpdatePackages] = useState(false);
  const [repo, setRepo] = useState('');
  const [repoGpgKeyUrl, setRepoGpgKeyUrl] = useState('');
  const [invalidFields, setInvalidFields] = useState([]);

  // Command
  const command = useSelector(selectCommand);

  // Plugins
  const [pluginValues, setPluginValues] = useState({});

  const handlePluginValue = useCallback(data => {
    setPluginValues(prevValues => ({ ...prevValues, ...data }));
  }, []);

  const handleInvalidField = useCallback((field, isValid) => {
    if (isValid) {
      setInvalidFields(prevFields => prevFields.filter(f => f !== field));
    } else {
      setInvalidFields(prevFields => {
        if (!prevFields.find(f => f === field)) {
          return [...prevFields, field].sort();
        }
        return prevFields;
      });
    }
  }, []);

  const handleSubmit = e => {
    e.preventDefault();

    const params = {
      organizationId,
      locationId,
      hostgroupId: hostGroupId,
      operatingsystemId: operatingSystemId,
      smartProxyId,
      insecure,
      setupRemoteExecution,
      setupInsights,
      jwtExpiration,
      packages,
      repo,
      repoGpgKeyUrl,
      updatePackages,
      ...pluginValues,
    };

    dispatch(commandAction(params));
  };

  const changeTab = (e, tab) => {
    e.preventDefault();
    setActiveTab(tab);
  };

  // Reset form values when Organization / Location is selected
  useEffect(() => {
    setHostGroupId();
    setOperatingSystemId();
    setSmartProxyId();

    dispatch(
      dataAction({ organization_id: organizationId, location_id: locationId })
    );
  }, [dispatch, organizationId, locationId]);

  useEffect(() => {
    if (hostGroupId === undefined && operatingSystemId === undefined) {
      return;
    }

    const params = {
      organization_id: organizationId,
      location_id: locationId,
      hostgroup_id: hostGroupId,
      operatingsystem_id: operatingSystemId,
    };

    dispatch(dataAction(params));

    // Disabled lint warning, need to check only hostgroup_id & operatingsystem_id
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [dispatch, hostGroupId, operatingSystemId]);

  return (
    <PageLayout
      header={__('Register Host')}
      searchable={false}
      toolbarButtons={
        <Button
          ouiaId="register-host-documentation-button"
          component="a"
          className="btn-docs"
          href={getDocsURL(
            'Managing_Hosts',
            'registering-a-host_managing-hosts'
          )}
          rel="noreferrer"
          target="_blank"
          variant="secondary"
        >
          {__(' Documentation')}
        </Button>
      }
    >
      <Form
        onSubmit={e => handleSubmit(e)}
        className="registration_commands_form"
        isHorizontal
      >
        <Grid hasGutter>
          <GridItem span={12}>
            <Tabs
              ouiaId="tabs-register-host"
              activeKey={activeTab}
              onSelect={(e, tab) => changeTab(e, tab)}
            >
              <Tab
                ouiaId="tab-general"
                eventKey={0}
                title={<TabTitleText>{__('General')}</TabTitleText>}
                tabContentId="generalTab"
                tabContentRef={generalTabRef}
              />
              <Tab
                ouiaId="tab-advanced"
                eventKey={1}
                title={<TabTitleText>{__('Advanced')}</TabTitleText>}
                tabContentId="advancedTab"
                tabContentRef={advancedTabRef}
              />
            </Tabs>
          </GridItem>

          {apiStatusData === STATUS.ERROR && (
            <>
              <GridItem span={4}>
                <Alert
                  ouiaId="alert-register-host-error"
                  variant="danger"
                  title={__(
                    'There was an error while loading the data, see the logs for more information.'
                  )}
                />
              </GridItem>
              <GridItem span={12} />
            </>
          )}
          <GridItem span={4}>
            <TabContent
              ouiaId="tab-content-register-host-general"
              eventKey={0}
              id="generalSection"
              ref={generalTabRef}
            >
              <div className="pf-c-form">
                <General
                  organizationId={organizationId}
                  organizations={organizations}
                  handleOrganization={setOrganizationId}
                  locationId={locationId}
                  locations={locations}
                  handleLocation={setLocationId}
                  hostGroupId={hostGroupId}
                  hostGroups={hostGroups}
                  handleHostGroup={setHostGroupId}
                  operatingSystemId={operatingSystemId}
                  operatingSystems={operatingSystems}
                  operatingSystemTemplate={operatingSystemTemplate}
                  handleOperatingSystem={setOperatingSystemId}
                  smartProxyId={smartProxyId}
                  smartProxies={smartProxies}
                  handleSmartProxy={setSmartProxyId}
                  insecure={insecure}
                  handleInsecure={setInsecure}
                  handleInvalidField={handleInvalidField}
                  invalidFields={invalidFields}
                  isLoading={isLoading}
                />

                <Slot
                  id="registrationGeneral"
                  organizationId={organizationId}
                  locationId={locationId}
                  hostGroupId={hostGroupId}
                  pluginValues={pluginValues}
                  pluginData={pluginData}
                  onChange={handlePluginValue}
                  handleInvalidField={handleInvalidField}
                  isLoading={isLoading}
                  configParams={configParams}
                  multi
                />
              </div>
            </TabContent>

            <TabContent
              ouiaId="tab-content-register-host-advanced"
              eventKey={1}
              id="advancedSection"
              ref={advancedTabRef}
              hidden
            >
              <div className="pf-c-form">
                <Advanced
                  configParams={configParams}
                  setupRemoteExecution={setupRemoteExecution}
                  setupInsights={setupInsights}
                  handleInsights={setSetupInsights}
                  handleRemoteExecution={setSetupRemoteExecution}
                  jwtExpiration={jwtExpiration}
                  handleJwtExpiration={setJwtExpiration}
                  handleInvalidField={handleInvalidField}
                  pluginValues={pluginValues}
                  handlePluginValue={handlePluginValue}
                  invalidFields={invalidFields}
                  organizationId={organizationId}
                  locationId={locationId}
                  hostGroupId={hostGroupId}
                  packages={packages}
                  handlePackages={setPackages}
                  repo={repo}
                  handleRepo={setRepo}
                  repoGpgKeyUrl={repoGpgKeyUrl}
                  handleRepoGpgKeyUrl={setRepoGpgKeyUrl}
                  updatePackages={updatePackages}
                  handleUpdatePackages={setUpdatePackages}
                  isLoading={isLoading}
                />
                <Slot
                  id="registrationAdvanced"
                  organizationId={organizationId}
                  locationId={locationId}
                  hostGroupId={hostGroupId}
                  pluginValues={pluginValues}
                  pluginData={pluginData}
                  onChange={handlePluginValue}
                  handleInvalidField={handleInvalidField}
                  isLoading={isLoading}
                  configParams={configParams}
                  multi
                />
              </div>
            </TabContent>
            <Actions
              isLoading={isLoading}
              isGenerating={isGenerating}
              handleSubmit={handleSubmit}
              invalidFields={invalidFields}
            />
          </GridItem>
          <GridItem span={10}>
            <Command apiStatus={apiStatusCommand} command={command} />
          </GridItem>
        </Grid>
      </Form>
    </PageLayout>
  );
};

export default RegistrationCommandsPage;