mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ui-library.git
				synced 2025-11-04 12:47:55 +00:00 
			
		
		
		
	Merge pull request #325 from Telecominfraproject/feature/NETEXP-2883-ap-details-redesign
NOJIRA: Move advanced settings to its own component
This commit is contained in:
		@@ -0,0 +1,507 @@
 | 
			
		||||
import React, { useEffect } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { Form, Select as AntdSelect } from 'antd';
 | 
			
		||||
import { Input, Select } from 'components/WithRoles';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import DisabledText from 'components/DisabledText';
 | 
			
		||||
import Tooltip from 'components/Tooltip';
 | 
			
		||||
 | 
			
		||||
import { sortRadioTypes } from 'utils/sortRadioTypes';
 | 
			
		||||
import { USER_FRIENDLY_RATES, USER_FRIENDLY_BANDWIDTHS } from '../../constants';
 | 
			
		||||
 | 
			
		||||
import styles from '../../../../index.module.scss';
 | 
			
		||||
 | 
			
		||||
const { Option } = AntdSelect;
 | 
			
		||||
 | 
			
		||||
const Advanced = ({ extraFields, childProfiles, data, radioTypes, form, Item }) => {
 | 
			
		||||
  const { details: { advancedRadioMap = {}, radioMap = {} } = {} } = data;
 | 
			
		||||
 | 
			
		||||
  const defaultOptions = (
 | 
			
		||||
    <Select className={styles.Field}>
 | 
			
		||||
      <Option value="enabled">Enabled</Option>
 | 
			
		||||
      <Option value="disabled">Disabled</Option>
 | 
			
		||||
    </Select>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const defaultOptionsBoolean = (
 | 
			
		||||
    <Select className={styles.Field}>
 | 
			
		||||
      <Option value="true">Enabled</Option>
 | 
			
		||||
      <Option value="false">Disabled</Option>
 | 
			
		||||
    </Select>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (data?.details) {
 | 
			
		||||
      const currentRadios = Object.keys(advancedRadioMap);
 | 
			
		||||
      const formData = {
 | 
			
		||||
        advancedRadioMap: {},
 | 
			
		||||
        radioMap: {},
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      currentRadios.forEach(radio => {
 | 
			
		||||
        formData.advancedRadioMap[radio] = {
 | 
			
		||||
          radioAdminState: advancedRadioMap[radio]?.radioAdminState || 'disabled',
 | 
			
		||||
          deauthAttackDetection: advancedRadioMap[radio]?.deauthAttackDetection ? 'true' : 'false',
 | 
			
		||||
          uapsdState: advancedRadioMap[radio]?.uapsdState || 'disabled',
 | 
			
		||||
          managementRate: {
 | 
			
		||||
            value: advancedRadioMap[radio]?.managementRate?.value || 'rate1mbps',
 | 
			
		||||
          },
 | 
			
		||||
          multicastRate: {
 | 
			
		||||
            value: advancedRadioMap[radio]?.multicastRate?.value || 'rate6mbps',
 | 
			
		||||
          },
 | 
			
		||||
          bestApSettings: {
 | 
			
		||||
            value: {
 | 
			
		||||
              dropInSnrPercentage:
 | 
			
		||||
                advancedRadioMap[radio]?.bestApSettings?.value?.dropInSnrPercentage || 0,
 | 
			
		||||
              minLoadFactor: advancedRadioMap[radio]?.bestApSettings?.value?.minLoadFactor || 0,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        formData.radioMap[radio] = {
 | 
			
		||||
          rxCellSizeDb: {
 | 
			
		||||
            value: radioMap[radio]?.rxCellSizeDb?.value || 0,
 | 
			
		||||
          },
 | 
			
		||||
          probeResponseThresholdDb: {
 | 
			
		||||
            value: radioMap[radio]?.probeResponseThresholdDb?.value || 0,
 | 
			
		||||
          },
 | 
			
		||||
          clientDisconnectThresholdDb: {
 | 
			
		||||
            value: radioMap[radio]?.clientDisconnectThresholdDb?.value || 0,
 | 
			
		||||
          },
 | 
			
		||||
          eirpTxPower: {
 | 
			
		||||
            value: radioMap[radio]?.eirpTxPower?.value || 0,
 | 
			
		||||
          },
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      form.setFieldsValue({ ...formData });
 | 
			
		||||
    }
 | 
			
		||||
  }, [data]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (data?.details) {
 | 
			
		||||
      const currentRadios = Object.keys(advancedRadioMap);
 | 
			
		||||
      const formData = {
 | 
			
		||||
        radioMap: {},
 | 
			
		||||
      };
 | 
			
		||||
      currentRadios.forEach(radio => {
 | 
			
		||||
        const isEnabled =
 | 
			
		||||
          childProfiles.rf?.[0]?.details?.rfConfigMap?.[radio]?.autoChannelSelection;
 | 
			
		||||
        formData.radioMap[radio] = {
 | 
			
		||||
          [isEnabled ? 'channelNumber' : 'manualChannelNumber']: isEnabled
 | 
			
		||||
            ? radioMap[radio]?.channelNumber
 | 
			
		||||
            : radioMap[radio]?.manualChannelNumber,
 | 
			
		||||
          [isEnabled ? 'backupChannelNumber' : 'manualBackupChannelNumber']: isEnabled
 | 
			
		||||
            ? radioMap[radio]?.backupChannelNumber
 | 
			
		||||
            : radioMap[radio]?.manualBackupChannelNumber,
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      form.setFieldsValue({ ...formData });
 | 
			
		||||
    }
 | 
			
		||||
  }, [data]);
 | 
			
		||||
 | 
			
		||||
  const renderItem = (label, obj = {}, dataIndex, renderInput, options = {}) => {
 | 
			
		||||
    if (extraFields.some(field => field.label === label)) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
    return (
 | 
			
		||||
      <Item
 | 
			
		||||
        label={label}
 | 
			
		||||
        colon={dataIndex !== 'radioType'}
 | 
			
		||||
        key={label}
 | 
			
		||||
        hidden={options.hidden ?? false}
 | 
			
		||||
      >
 | 
			
		||||
        <div className={styles.InlineDiv}>
 | 
			
		||||
          {sortRadioTypes(Object.keys(obj)).map(i =>
 | 
			
		||||
            renderInput ? (
 | 
			
		||||
              renderInput(dataIndex, i, label, options)
 | 
			
		||||
            ) : (
 | 
			
		||||
              <span key={i} className={styles.spanStyle}>
 | 
			
		||||
                {dataIndex === 'radioType'
 | 
			
		||||
                  ? radioTypes?.[obj[i]?.[dataIndex]]
 | 
			
		||||
                  : obj[i]?.[dataIndex]}
 | 
			
		||||
              </span>
 | 
			
		||||
            )
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Item>
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const renderConditionalItem = (label, obj = {}, dataIndex, dependency) => (
 | 
			
		||||
    <Item label={label} key={label}>
 | 
			
		||||
      <div className={styles.InlineDiv}>
 | 
			
		||||
        {sortRadioTypes(Object.keys(obj)).map(key => {
 | 
			
		||||
          const isEnabled = childProfiles.rf?.[0]?.details?.rfConfigMap?.[key]?.[dependency];
 | 
			
		||||
 | 
			
		||||
          if (isEnabled) {
 | 
			
		||||
            return (
 | 
			
		||||
              <DisabledText
 | 
			
		||||
                key={key}
 | 
			
		||||
                value={
 | 
			
		||||
                  USER_FRIENDLY_RATES[obj[key]?.[dataIndex]?.value] ||
 | 
			
		||||
                  obj[key]?.[dataIndex]?.value ||
 | 
			
		||||
                  'N/A'
 | 
			
		||||
                }
 | 
			
		||||
                title={`The ${radioTypes[key]} radio has "${_.startCase(
 | 
			
		||||
                  dependency
 | 
			
		||||
                )}" enabled in the RF Profile`}
 | 
			
		||||
                text="Auto"
 | 
			
		||||
              />
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
          return (
 | 
			
		||||
            <DisabledText
 | 
			
		||||
              key={key}
 | 
			
		||||
              value={
 | 
			
		||||
                USER_FRIENDLY_RATES[
 | 
			
		||||
                  childProfiles.rf?.[0]?.details?.rfConfigMap?.[key]?.[dataIndex]
 | 
			
		||||
                ] || childProfiles.rf?.[0]?.details?.rfConfigMap?.[key]?.[dataIndex || 'N/A']
 | 
			
		||||
              }
 | 
			
		||||
              title={`The ${radioTypes[key]} radio has "${_.startCase(
 | 
			
		||||
                dependency
 | 
			
		||||
              )}" disabled in the RF Profile`}
 | 
			
		||||
              text="Profile"
 | 
			
		||||
            />
 | 
			
		||||
          );
 | 
			
		||||
        })}
 | 
			
		||||
      </div>
 | 
			
		||||
    </Item>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const renderInputItem = (dataIndex, key, label, options = {}) => (
 | 
			
		||||
    <Item
 | 
			
		||||
      key={options.mapName + key + dataIndex}
 | 
			
		||||
      name={[options.mapName, key, ...dataIndex]}
 | 
			
		||||
      rules={[
 | 
			
		||||
        { required: true, message: options.error },
 | 
			
		||||
        ({ getFieldValue }) => ({
 | 
			
		||||
          validator(_rule, value) {
 | 
			
		||||
            if (
 | 
			
		||||
              !value ||
 | 
			
		||||
              (getFieldValue([options.mapName, key, ...dataIndex]) <= options.max &&
 | 
			
		||||
                getFieldValue([options.mapName, key, ...dataIndex]) >= options.min)
 | 
			
		||||
            ) {
 | 
			
		||||
              return Promise.resolve();
 | 
			
		||||
            }
 | 
			
		||||
            return Promise.reject(new Error(options.error));
 | 
			
		||||
          },
 | 
			
		||||
        }),
 | 
			
		||||
      ]}
 | 
			
		||||
    >
 | 
			
		||||
      <Input
 | 
			
		||||
        className={styles.Field}
 | 
			
		||||
        placeholder={`Enter ${label} for ${radioTypes[key]}`}
 | 
			
		||||
        type="number"
 | 
			
		||||
        min={options.min}
 | 
			
		||||
        max={options.max}
 | 
			
		||||
        addonAfter={options?.addOnText ? options?.addOnText : ''}
 | 
			
		||||
      />
 | 
			
		||||
    </Item>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const renderOptionItem = (dataIndex, key, label, options = {}) => {
 | 
			
		||||
    return (
 | 
			
		||||
      <Item
 | 
			
		||||
        key={key + dataIndex}
 | 
			
		||||
        name={[options.mapName, key, ...dataIndex]}
 | 
			
		||||
        rules={[
 | 
			
		||||
          {
 | 
			
		||||
            required: true,
 | 
			
		||||
            message: `Enter ${label} for ${radioTypes[key]}`,
 | 
			
		||||
          },
 | 
			
		||||
        ]}
 | 
			
		||||
      >
 | 
			
		||||
        {typeof options.dropdown === 'function' ? options.dropdown(key) : options.dropdown}
 | 
			
		||||
      </Item>
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const renderChannelItem = label => {
 | 
			
		||||
    return (
 | 
			
		||||
      <Item label={label} colon={false}>
 | 
			
		||||
        <div className={styles.InlineDiv}>
 | 
			
		||||
          {sortRadioTypes(Object.keys(radioMap)).map(key => {
 | 
			
		||||
            const isEnabled = childProfiles.rf?.[0]?.details?.rfConfigMap[key].autoChannelSelection;
 | 
			
		||||
 | 
			
		||||
            let channel;
 | 
			
		||||
            if (label === 'Active Channel') {
 | 
			
		||||
              channel = isEnabled
 | 
			
		||||
                ? {
 | 
			
		||||
                    dataIndex: 'channelNumber',
 | 
			
		||||
                    addOnText: (
 | 
			
		||||
                      <Tooltip
 | 
			
		||||
                        text="Auto"
 | 
			
		||||
                        title={`The ${radioTypes[key]} radio has "Auto Channel Selection" enabled in the RF Profile`}
 | 
			
		||||
                      />
 | 
			
		||||
                    ),
 | 
			
		||||
                  }
 | 
			
		||||
                : {
 | 
			
		||||
                    dataIndex: 'manualChannelNumber',
 | 
			
		||||
                    addOnText: 'Manual',
 | 
			
		||||
                    dependencies: ['radioMap', key, 'manualBackupChannelNumber'],
 | 
			
		||||
                  };
 | 
			
		||||
            }
 | 
			
		||||
            if (label === 'Backup Channel') {
 | 
			
		||||
              channel = isEnabled
 | 
			
		||||
                ? {
 | 
			
		||||
                    dataIndex: 'backupChannelNumber',
 | 
			
		||||
                    addOnText: (
 | 
			
		||||
                      <Tooltip
 | 
			
		||||
                        text="Auto"
 | 
			
		||||
                        title={`The ${radioTypes[key]} radio has "Auto Channel Selection" enabled in the RF Profile`}
 | 
			
		||||
                      />
 | 
			
		||||
                    ),
 | 
			
		||||
                  }
 | 
			
		||||
                : {
 | 
			
		||||
                    dataIndex: 'manualBackupChannelNumber',
 | 
			
		||||
                    addOnText: 'Manual',
 | 
			
		||||
                    dependencies: ['radioMap', key, 'manualChannelNumber'],
 | 
			
		||||
                  };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const powerLevels = data?.details?.radioMap?.[key]?.allowedChannelsPowerLevels ?? [];
 | 
			
		||||
 | 
			
		||||
            const allowedChannels = powerLevels
 | 
			
		||||
              .filter(item => {
 | 
			
		||||
                if (channel.dataIndex === 'manualBackupChannelNumber') {
 | 
			
		||||
                  return !item.dfs;
 | 
			
		||||
                }
 | 
			
		||||
                return item;
 | 
			
		||||
              })
 | 
			
		||||
              .map(item => item?.channelNumber)
 | 
			
		||||
              .sort((a, b) => a - b);
 | 
			
		||||
 | 
			
		||||
            return (
 | 
			
		||||
              <Item
 | 
			
		||||
                key={`radioMap${key}${channel.dataIndex}`}
 | 
			
		||||
                name={['radioMap', key, channel.dataIndex]}
 | 
			
		||||
                dependencies={[channel.dependencies]}
 | 
			
		||||
                rules={[
 | 
			
		||||
                  { required: true, message: `Allowed Channels: ${allowedChannels.join(', ')}` },
 | 
			
		||||
                  ({ getFieldValue }) => ({
 | 
			
		||||
                    validator(_rule, value) {
 | 
			
		||||
                      if (!isEnabled) {
 | 
			
		||||
                        if (
 | 
			
		||||
                          parseInt(getFieldValue(['radioMap', key, 'manualChannelNumber']), 10) ===
 | 
			
		||||
                          parseInt(
 | 
			
		||||
                            getFieldValue(['radioMap', key, 'manualBackupChannelNumber']),
 | 
			
		||||
                            10
 | 
			
		||||
                          )
 | 
			
		||||
                        ) {
 | 
			
		||||
                          return Promise.reject(
 | 
			
		||||
                            new Error('Active and backup channels must be different')
 | 
			
		||||
                          );
 | 
			
		||||
                        }
 | 
			
		||||
                        const channelNumber = parseInt(
 | 
			
		||||
                          getFieldValue(['radioMap', key, channel.dataIndex]),
 | 
			
		||||
                          10
 | 
			
		||||
                        );
 | 
			
		||||
                        if (!value || allowedChannels.includes(channelNumber)) {
 | 
			
		||||
                          return Promise.resolve();
 | 
			
		||||
                        }
 | 
			
		||||
                        return Promise.reject(
 | 
			
		||||
                          new Error(`Allowed Channels: ${allowedChannels.join(', ')}`)
 | 
			
		||||
                        );
 | 
			
		||||
                      }
 | 
			
		||||
                      return Promise.resolve();
 | 
			
		||||
                    },
 | 
			
		||||
                  }),
 | 
			
		||||
                ]}
 | 
			
		||||
              >
 | 
			
		||||
                <Input
 | 
			
		||||
                  className={styles.Field}
 | 
			
		||||
                  placeholder={`Enter ${label} for ${radioTypes[key]}`}
 | 
			
		||||
                  type="number"
 | 
			
		||||
                  min={Math.min(...allowedChannels)}
 | 
			
		||||
                  max={Math.max(...allowedChannels)}
 | 
			
		||||
                  addonAfter={channel.addOnText}
 | 
			
		||||
                  disabled={isEnabled}
 | 
			
		||||
                />
 | 
			
		||||
              </Item>
 | 
			
		||||
            );
 | 
			
		||||
          })}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Item>
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const renderBandwidthLabels = () => (
 | 
			
		||||
    <Item label="Channel Bandwidth">
 | 
			
		||||
      <div className={styles.InlineDiv}>
 | 
			
		||||
        {sortRadioTypes(Object.keys(radioMap)).map(radio => (
 | 
			
		||||
          <DisabledText
 | 
			
		||||
            key={radio}
 | 
			
		||||
            value={
 | 
			
		||||
              USER_FRIENDLY_BANDWIDTHS[
 | 
			
		||||
                childProfiles?.rf?.[0]?.details?.rfConfigMap?.[radio].channelBandwidth
 | 
			
		||||
              ] ?? 'N/A'
 | 
			
		||||
            }
 | 
			
		||||
            showTooltip={false}
 | 
			
		||||
          />
 | 
			
		||||
        ))}
 | 
			
		||||
      </div>
 | 
			
		||||
    </Item>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      {renderItem(' ', data?.details?.radioMap, 'radioType')}
 | 
			
		||||
      <p>Radio Specific Parameters:</p>
 | 
			
		||||
      {renderBandwidthLabels()}
 | 
			
		||||
      {renderItem('Enable Radio', advancedRadioMap, ['radioAdminState'], renderOptionItem, {
 | 
			
		||||
        dropdown: defaultOptions,
 | 
			
		||||
        mapName: 'advancedRadioMap',
 | 
			
		||||
      })}
 | 
			
		||||
      {renderItem(
 | 
			
		||||
        'Deauth Attack Detection',
 | 
			
		||||
        advancedRadioMap,
 | 
			
		||||
        ['deauthAttackDetection'],
 | 
			
		||||
        renderOptionItem,
 | 
			
		||||
        {
 | 
			
		||||
          mapName: 'advancedRadioMap',
 | 
			
		||||
          dropdown: defaultOptionsBoolean,
 | 
			
		||||
        }
 | 
			
		||||
      )}
 | 
			
		||||
      {renderItem('UAPSD', advancedRadioMap, ['uapsdState'], renderOptionItem, {
 | 
			
		||||
        mapName: 'advancedRadioMap',
 | 
			
		||||
        dropdown: defaultOptions,
 | 
			
		||||
      })}
 | 
			
		||||
      {renderChannelItem('Active Channel')}
 | 
			
		||||
      {renderChannelItem('Backup Channel')}
 | 
			
		||||
      {extraFields.map(field =>
 | 
			
		||||
        renderConditionalItem(field.label, field.obj, field.dataIndex, field.dependencies)
 | 
			
		||||
      )}
 | 
			
		||||
      {renderItem(
 | 
			
		||||
        'Management Rate (Mbps)',
 | 
			
		||||
        advancedRadioMap,
 | 
			
		||||
        ['managementRate', 'value'],
 | 
			
		||||
        renderOptionItem,
 | 
			
		||||
        {
 | 
			
		||||
          mapName: 'advancedRadioMap',
 | 
			
		||||
          dropdown: key => (
 | 
			
		||||
            <Select className={styles.Field}>
 | 
			
		||||
              {key === 'is2dot4GHz' && (
 | 
			
		||||
                <>
 | 
			
		||||
                  <Option value="rate1mbps">1</Option>
 | 
			
		||||
                  <Option value="rate2mbps">2</Option>
 | 
			
		||||
                  <Option value="rate5dot5mbps">5.5</Option>
 | 
			
		||||
                </>
 | 
			
		||||
              )}
 | 
			
		||||
              <Option value="rate6mbps">6</Option>
 | 
			
		||||
              <Option value="rate9mbps">9</Option>
 | 
			
		||||
              {key === 'is2dot4GHz' && <Option value="rate11mbps">11</Option>}
 | 
			
		||||
              <Option value="rate12mbps">12</Option>
 | 
			
		||||
              <Option value="rate18mbps">18</Option>
 | 
			
		||||
              <Option value="rate24mbps">24</Option>
 | 
			
		||||
            </Select>
 | 
			
		||||
          ),
 | 
			
		||||
        }
 | 
			
		||||
      )}
 | 
			
		||||
      {renderItem(
 | 
			
		||||
        'Multicast Rate (Mbps)',
 | 
			
		||||
        advancedRadioMap,
 | 
			
		||||
        ['multicastRate', 'value'],
 | 
			
		||||
        renderOptionItem,
 | 
			
		||||
        {
 | 
			
		||||
          mapName: 'advancedRadioMap',
 | 
			
		||||
          dropdown: (
 | 
			
		||||
            <Select className={styles.Field}>
 | 
			
		||||
              <Option value="rate6mbps">6</Option>
 | 
			
		||||
              <Option value="rate9mbps">9</Option>
 | 
			
		||||
              <Option value="rate12mbps">12</Option>
 | 
			
		||||
              <Option value="rate18mbps">18</Option>
 | 
			
		||||
              <Option value="rate24mbps">24</Option>
 | 
			
		||||
              <Option value="rate36mbps">36</Option>
 | 
			
		||||
              <Option value="rate48mbps">48</Option>
 | 
			
		||||
              <Option value="rate54mbps">54</Option>
 | 
			
		||||
            </Select>
 | 
			
		||||
          ),
 | 
			
		||||
        }
 | 
			
		||||
      )}
 | 
			
		||||
      {renderItem(
 | 
			
		||||
        'Probe Response Threshold',
 | 
			
		||||
        radioMap,
 | 
			
		||||
        ['probeResponseThresholdDb', 'value'],
 | 
			
		||||
        renderInputItem,
 | 
			
		||||
        {
 | 
			
		||||
          min: -100,
 | 
			
		||||
          max: -40,
 | 
			
		||||
          error: '-100 - -40 dBm',
 | 
			
		||||
          addOnText: 'dBm',
 | 
			
		||||
          mapName: 'radioMap',
 | 
			
		||||
        }
 | 
			
		||||
      )}
 | 
			
		||||
      {renderItem(
 | 
			
		||||
        'Client Disconnect Threshold',
 | 
			
		||||
        radioMap,
 | 
			
		||||
        ['clientDisconnectThresholdDb', 'value'],
 | 
			
		||||
        renderInputItem,
 | 
			
		||||
        {
 | 
			
		||||
          min: -100,
 | 
			
		||||
          max: 0,
 | 
			
		||||
          error: '-100 - 0 dBm',
 | 
			
		||||
          addOnText: 'dBm',
 | 
			
		||||
          mapName: 'radioMap',
 | 
			
		||||
        }
 | 
			
		||||
      )}
 | 
			
		||||
      {renderItem('EIRP Tx Power', radioMap, ['eirpTxPower', 'value'], renderInputItem, {
 | 
			
		||||
        min: 1,
 | 
			
		||||
        max: 32,
 | 
			
		||||
        error: '1 - 32 dBm',
 | 
			
		||||
        addOnText: 'dBm',
 | 
			
		||||
        mapName: 'radioMap',
 | 
			
		||||
      })}
 | 
			
		||||
 | 
			
		||||
      {renderItem(
 | 
			
		||||
        'SNR',
 | 
			
		||||
        advancedRadioMap,
 | 
			
		||||
        ['bestApSettings', 'value', 'dropInSnrPercentage'],
 | 
			
		||||
        renderInputItem,
 | 
			
		||||
        {
 | 
			
		||||
          min: 0,
 | 
			
		||||
          max: 100,
 | 
			
		||||
          error: '0 - 100%',
 | 
			
		||||
          addOnText: '% Drop',
 | 
			
		||||
          mapName: 'advancedRadioMap',
 | 
			
		||||
          hidden: true,
 | 
			
		||||
        }
 | 
			
		||||
      )}
 | 
			
		||||
      {renderItem(
 | 
			
		||||
        'Min Load',
 | 
			
		||||
        advancedRadioMap,
 | 
			
		||||
        ['bestApSettings', 'value', 'minLoadFactor'],
 | 
			
		||||
        renderInputItem,
 | 
			
		||||
        {
 | 
			
		||||
          min: 0,
 | 
			
		||||
          max: 100,
 | 
			
		||||
          error: '0 - 100%',
 | 
			
		||||
          addOnText: '%',
 | 
			
		||||
          mapName: 'advancedRadioMap',
 | 
			
		||||
          hidden: true,
 | 
			
		||||
        }
 | 
			
		||||
      )}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Advanced.propTypes = {
 | 
			
		||||
  extraFields: PropTypes.instanceOf(Array),
 | 
			
		||||
  data: PropTypes.instanceOf(Object),
 | 
			
		||||
  childProfiles: PropTypes.instanceOf(Object),
 | 
			
		||||
  radioTypes: PropTypes.instanceOf(Object),
 | 
			
		||||
  form: PropTypes.instanceOf(Object),
 | 
			
		||||
  Item: PropTypes.node,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Advanced.defaultProps = {
 | 
			
		||||
  extraFields: [],
 | 
			
		||||
  data: {},
 | 
			
		||||
  childProfiles: {},
 | 
			
		||||
  radioTypes: {},
 | 
			
		||||
  form: null,
 | 
			
		||||
  Item: Form.Item,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Advanced;
 | 
			
		||||
@@ -11,16 +11,14 @@ import {
 | 
			
		||||
  Empty,
 | 
			
		||||
  Typography,
 | 
			
		||||
} from 'antd';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { Card } from 'components/Skeleton';
 | 
			
		||||
import { Input, Select, RoleProtectedBtn } from 'components/WithRoles';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import ThemeContext from 'contexts/ThemeContext';
 | 
			
		||||
import DisabledText from 'components/DisabledText';
 | 
			
		||||
import Tooltip from 'components/Tooltip';
 | 
			
		||||
 | 
			
		||||
import { sortRadioTypes } from 'utils/sortRadioTypes';
 | 
			
		||||
import { pageLayout } from 'utils/form';
 | 
			
		||||
import { USER_FRIENDLY_RATES, USER_FRIENDLY_BANDWIDTHS } from './constants';
 | 
			
		||||
 | 
			
		||||
import Advanced from './components/Advanced';
 | 
			
		||||
 | 
			
		||||
import styles from '../../index.module.scss';
 | 
			
		||||
 | 
			
		||||
@@ -112,80 +110,8 @@ const General = ({
 | 
			
		||||
    longitude,
 | 
			
		||||
    serial,
 | 
			
		||||
    lastModifiedTimestamp,
 | 
			
		||||
    details: { advancedRadioMap = {}, radioMap = {} } = {},
 | 
			
		||||
  } = data;
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (data?.details) {
 | 
			
		||||
      const currentRadios = Object.keys(advancedRadioMap);
 | 
			
		||||
      const formData = {
 | 
			
		||||
        advancedRadioMap: {},
 | 
			
		||||
        radioMap: {},
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      currentRadios.forEach(radio => {
 | 
			
		||||
        formData.advancedRadioMap[radio] = {
 | 
			
		||||
          radioAdminState: advancedRadioMap[radio]?.radioAdminState || 'disabled',
 | 
			
		||||
          deauthAttackDetection: advancedRadioMap[radio]?.deauthAttackDetection ? 'true' : 'false',
 | 
			
		||||
          uapsdState: advancedRadioMap[radio]?.uapsdState || 'disabled',
 | 
			
		||||
          managementRate: {
 | 
			
		||||
            value: advancedRadioMap[radio]?.managementRate?.value || 'rate1mbps',
 | 
			
		||||
          },
 | 
			
		||||
          multicastRate: {
 | 
			
		||||
            value: advancedRadioMap[radio]?.multicastRate?.value || 'rate6mbps',
 | 
			
		||||
          },
 | 
			
		||||
          bestApSettings: {
 | 
			
		||||
            value: {
 | 
			
		||||
              dropInSnrPercentage:
 | 
			
		||||
                advancedRadioMap[radio]?.bestApSettings?.value?.dropInSnrPercentage || 0,
 | 
			
		||||
              minLoadFactor: advancedRadioMap[radio]?.bestApSettings?.value?.minLoadFactor || 0,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        formData.radioMap[radio] = {
 | 
			
		||||
          rxCellSizeDb: {
 | 
			
		||||
            value: radioMap[radio]?.rxCellSizeDb?.value || 0,
 | 
			
		||||
          },
 | 
			
		||||
          probeResponseThresholdDb: {
 | 
			
		||||
            value: radioMap[radio]?.probeResponseThresholdDb?.value || 0,
 | 
			
		||||
          },
 | 
			
		||||
          clientDisconnectThresholdDb: {
 | 
			
		||||
            value: radioMap[radio]?.clientDisconnectThresholdDb?.value || 0,
 | 
			
		||||
          },
 | 
			
		||||
          eirpTxPower: {
 | 
			
		||||
            value: radioMap[radio]?.eirpTxPower?.value || 0,
 | 
			
		||||
          },
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      form.setFieldsValue({ ...formData });
 | 
			
		||||
    }
 | 
			
		||||
  }, [data]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (data?.details) {
 | 
			
		||||
      const currentRadios = Object.keys(advancedRadioMap);
 | 
			
		||||
      const formData = {
 | 
			
		||||
        radioMap: {},
 | 
			
		||||
      };
 | 
			
		||||
      currentRadios.forEach(radio => {
 | 
			
		||||
        const isEnabled =
 | 
			
		||||
          childProfiles.rf?.[0]?.details?.rfConfigMap?.[radio]?.autoChannelSelection;
 | 
			
		||||
        formData.radioMap[radio] = {
 | 
			
		||||
          [isEnabled ? 'channelNumber' : 'manualChannelNumber']: isEnabled
 | 
			
		||||
            ? radioMap[radio]?.channelNumber
 | 
			
		||||
            : radioMap[radio]?.manualChannelNumber,
 | 
			
		||||
          [isEnabled ? 'backupChannelNumber' : 'manualBackupChannelNumber']: isEnabled
 | 
			
		||||
            ? radioMap[radio]?.backupChannelNumber
 | 
			
		||||
            : radioMap[radio]?.manualBackupChannelNumber,
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      form.setFieldsValue({ ...formData });
 | 
			
		||||
    }
 | 
			
		||||
  }, [selectedProfile]);
 | 
			
		||||
 | 
			
		||||
  const handleOnSave = () => {
 | 
			
		||||
    form
 | 
			
		||||
      .validateFields()
 | 
			
		||||
@@ -235,264 +161,6 @@ const General = ({
 | 
			
		||||
      });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const defaultOptions = (
 | 
			
		||||
    <Select className={styles.Field}>
 | 
			
		||||
      <Option value="enabled">Enabled</Option>
 | 
			
		||||
      <Option value="disabled">Disabled</Option>
 | 
			
		||||
    </Select>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const defaultOptionsBoolean = (
 | 
			
		||||
    <Select className={styles.Field}>
 | 
			
		||||
      <Option value="true">Enabled</Option>
 | 
			
		||||
      <Option value="false">Disabled</Option>
 | 
			
		||||
    </Select>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const renderItem = (label, obj = {}, dataIndex, renderInput, options = {}) => {
 | 
			
		||||
    if (extraFields.some(field => field.label === label)) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
    return (
 | 
			
		||||
      <Item
 | 
			
		||||
        label={label}
 | 
			
		||||
        colon={dataIndex !== 'radioType'}
 | 
			
		||||
        key={label}
 | 
			
		||||
        hidden={options.hidden ?? false}
 | 
			
		||||
      >
 | 
			
		||||
        <div className={styles.InlineDiv}>
 | 
			
		||||
          {sortRadioTypes(Object.keys(obj)).map(i =>
 | 
			
		||||
            renderInput ? (
 | 
			
		||||
              renderInput(dataIndex, i, label, options)
 | 
			
		||||
            ) : (
 | 
			
		||||
              <span key={i} className={styles.spanStyle}>
 | 
			
		||||
                {dataIndex === 'radioType'
 | 
			
		||||
                  ? radioTypes?.[obj[i]?.[dataIndex]]
 | 
			
		||||
                  : obj[i]?.[dataIndex]}
 | 
			
		||||
              </span>
 | 
			
		||||
            )
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Item>
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const renderConditionalItem = (label, obj = {}, dataIndex, dependency) => (
 | 
			
		||||
    <Item label={label} key={label}>
 | 
			
		||||
      <div className={styles.InlineDiv}>
 | 
			
		||||
        {sortRadioTypes(Object.keys(obj)).map(key => {
 | 
			
		||||
          const isEnabled = childProfiles.rf?.[0]?.details?.rfConfigMap?.[key]?.[dependency];
 | 
			
		||||
 | 
			
		||||
          if (isEnabled) {
 | 
			
		||||
            return (
 | 
			
		||||
              <DisabledText
 | 
			
		||||
                key={key}
 | 
			
		||||
                value={
 | 
			
		||||
                  USER_FRIENDLY_RATES[obj[key]?.[dataIndex]?.value] ||
 | 
			
		||||
                  obj[key]?.[dataIndex]?.value ||
 | 
			
		||||
                  'N/A'
 | 
			
		||||
                }
 | 
			
		||||
                title={`The ${radioTypes[key]} radio has "${_.startCase(
 | 
			
		||||
                  dependency
 | 
			
		||||
                )}" enabled in the RF Profile`}
 | 
			
		||||
                text="Auto"
 | 
			
		||||
              />
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
          return (
 | 
			
		||||
            <DisabledText
 | 
			
		||||
              key={key}
 | 
			
		||||
              value={
 | 
			
		||||
                USER_FRIENDLY_RATES[
 | 
			
		||||
                  childProfiles.rf?.[0]?.details?.rfConfigMap?.[key]?.[dataIndex]
 | 
			
		||||
                ] || childProfiles.rf?.[0]?.details?.rfConfigMap?.[key]?.[dataIndex || 'N/A']
 | 
			
		||||
              }
 | 
			
		||||
              title={`The ${radioTypes[key]} radio has "${_.startCase(
 | 
			
		||||
                dependency
 | 
			
		||||
              )}" disabled in the RF Profile`}
 | 
			
		||||
              text="Profile"
 | 
			
		||||
            />
 | 
			
		||||
          );
 | 
			
		||||
        })}
 | 
			
		||||
      </div>
 | 
			
		||||
    </Item>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const renderInputItem = (dataIndex, key, label, options = {}) => (
 | 
			
		||||
    <Item
 | 
			
		||||
      key={options.mapName + key + dataIndex}
 | 
			
		||||
      name={[options.mapName, key, ...dataIndex]}
 | 
			
		||||
      rules={[
 | 
			
		||||
        { required: true, message: options.error },
 | 
			
		||||
        ({ getFieldValue }) => ({
 | 
			
		||||
          validator(_rule, value) {
 | 
			
		||||
            if (
 | 
			
		||||
              !value ||
 | 
			
		||||
              (getFieldValue([options.mapName, key, ...dataIndex]) <= options.max &&
 | 
			
		||||
                getFieldValue([options.mapName, key, ...dataIndex]) >= options.min)
 | 
			
		||||
            ) {
 | 
			
		||||
              return Promise.resolve();
 | 
			
		||||
            }
 | 
			
		||||
            return Promise.reject(new Error(options.error));
 | 
			
		||||
          },
 | 
			
		||||
        }),
 | 
			
		||||
      ]}
 | 
			
		||||
    >
 | 
			
		||||
      <Input
 | 
			
		||||
        className={styles.Field}
 | 
			
		||||
        placeholder={`Enter ${label} for ${radioTypes[key]}`}
 | 
			
		||||
        type="number"
 | 
			
		||||
        min={options.min}
 | 
			
		||||
        max={options.max}
 | 
			
		||||
        addonAfter={options?.addOnText ? options?.addOnText : ''}
 | 
			
		||||
      />
 | 
			
		||||
    </Item>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const renderOptionItem = (dataIndex, key, label, options = {}) => {
 | 
			
		||||
    return (
 | 
			
		||||
      <Item
 | 
			
		||||
        key={key + dataIndex}
 | 
			
		||||
        name={[options.mapName, key, ...dataIndex]}
 | 
			
		||||
        rules={[
 | 
			
		||||
          {
 | 
			
		||||
            required: true,
 | 
			
		||||
            message: `Enter ${label} for ${radioTypes[key]}`,
 | 
			
		||||
          },
 | 
			
		||||
        ]}
 | 
			
		||||
      >
 | 
			
		||||
        {typeof options.dropdown === 'function' ? options.dropdown(key) : options.dropdown}
 | 
			
		||||
      </Item>
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const renderChannelItem = label => {
 | 
			
		||||
    return (
 | 
			
		||||
      <Item label={label} colon={false}>
 | 
			
		||||
        <div className={styles.InlineDiv}>
 | 
			
		||||
          {sortRadioTypes(Object.keys(radioMap)).map(key => {
 | 
			
		||||
            const isEnabled = childProfiles.rf?.[0]?.details?.rfConfigMap[key].autoChannelSelection;
 | 
			
		||||
 | 
			
		||||
            let channel;
 | 
			
		||||
            if (label === 'Active Channel') {
 | 
			
		||||
              channel = isEnabled
 | 
			
		||||
                ? {
 | 
			
		||||
                    dataIndex: 'channelNumber',
 | 
			
		||||
                    addOnText: (
 | 
			
		||||
                      <Tooltip
 | 
			
		||||
                        text="Auto"
 | 
			
		||||
                        title={`The ${radioTypes[key]} radio has "Auto Channel Selection" enabled in the RF Profile`}
 | 
			
		||||
                      />
 | 
			
		||||
                    ),
 | 
			
		||||
                  }
 | 
			
		||||
                : {
 | 
			
		||||
                    dataIndex: 'manualChannelNumber',
 | 
			
		||||
                    addOnText: 'Manual',
 | 
			
		||||
                    dependencies: ['radioMap', key, 'manualBackupChannelNumber'],
 | 
			
		||||
                  };
 | 
			
		||||
            }
 | 
			
		||||
            if (label === 'Backup Channel') {
 | 
			
		||||
              channel = isEnabled
 | 
			
		||||
                ? {
 | 
			
		||||
                    dataIndex: 'backupChannelNumber',
 | 
			
		||||
                    addOnText: (
 | 
			
		||||
                      <Tooltip
 | 
			
		||||
                        text="Auto"
 | 
			
		||||
                        title={`The ${radioTypes[key]} radio has "Auto Channel Selection" enabled in the RF Profile`}
 | 
			
		||||
                      />
 | 
			
		||||
                    ),
 | 
			
		||||
                  }
 | 
			
		||||
                : {
 | 
			
		||||
                    dataIndex: 'manualBackupChannelNumber',
 | 
			
		||||
                    addOnText: 'Manual',
 | 
			
		||||
                    dependencies: ['radioMap', key, 'manualChannelNumber'],
 | 
			
		||||
                  };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const powerLevels = data?.details?.radioMap?.[key]?.allowedChannelsPowerLevels ?? [];
 | 
			
		||||
 | 
			
		||||
            const allowedChannels = powerLevels
 | 
			
		||||
              .filter(item => {
 | 
			
		||||
                if (channel.dataIndex === 'manualBackupChannelNumber') {
 | 
			
		||||
                  return !item.dfs;
 | 
			
		||||
                }
 | 
			
		||||
                return item;
 | 
			
		||||
              })
 | 
			
		||||
              .map(item => item?.channelNumber)
 | 
			
		||||
              .sort((a, b) => a - b);
 | 
			
		||||
 | 
			
		||||
            return (
 | 
			
		||||
              <Item
 | 
			
		||||
                key={`radioMap${key}${channel.dataIndex}`}
 | 
			
		||||
                name={['radioMap', key, channel.dataIndex]}
 | 
			
		||||
                dependencies={[channel.dependencies]}
 | 
			
		||||
                rules={[
 | 
			
		||||
                  { required: true, message: `Allowed Channels: ${allowedChannels.join(', ')}` },
 | 
			
		||||
                  ({ getFieldValue }) => ({
 | 
			
		||||
                    validator(_rule, value) {
 | 
			
		||||
                      if (!isEnabled) {
 | 
			
		||||
                        if (
 | 
			
		||||
                          parseInt(getFieldValue(['radioMap', key, 'manualChannelNumber']), 10) ===
 | 
			
		||||
                          parseInt(
 | 
			
		||||
                            getFieldValue(['radioMap', key, 'manualBackupChannelNumber']),
 | 
			
		||||
                            10
 | 
			
		||||
                          )
 | 
			
		||||
                        ) {
 | 
			
		||||
                          return Promise.reject(
 | 
			
		||||
                            new Error('Active and backup channels must be different')
 | 
			
		||||
                          );
 | 
			
		||||
                        }
 | 
			
		||||
                        const channelNumber = parseInt(
 | 
			
		||||
                          getFieldValue(['radioMap', key, channel.dataIndex]),
 | 
			
		||||
                          10
 | 
			
		||||
                        );
 | 
			
		||||
                        if (!value || allowedChannels.includes(channelNumber)) {
 | 
			
		||||
                          return Promise.resolve();
 | 
			
		||||
                        }
 | 
			
		||||
                        return Promise.reject(
 | 
			
		||||
                          new Error(`Allowed Channels: ${allowedChannels.join(', ')}`)
 | 
			
		||||
                        );
 | 
			
		||||
                      }
 | 
			
		||||
                      return Promise.resolve();
 | 
			
		||||
                    },
 | 
			
		||||
                  }),
 | 
			
		||||
                ]}
 | 
			
		||||
              >
 | 
			
		||||
                <Input
 | 
			
		||||
                  className={styles.Field}
 | 
			
		||||
                  placeholder={`Enter ${label} for ${radioTypes[key]}`}
 | 
			
		||||
                  type="number"
 | 
			
		||||
                  min={Math.min(...allowedChannels)}
 | 
			
		||||
                  max={Math.max(...allowedChannels)}
 | 
			
		||||
                  addonAfter={channel.addOnText}
 | 
			
		||||
                  disabled={isEnabled}
 | 
			
		||||
                />
 | 
			
		||||
              </Item>
 | 
			
		||||
            );
 | 
			
		||||
          })}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Item>
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const renderBandwidthLabels = () => (
 | 
			
		||||
    <Item label="Channel Bandwidth">
 | 
			
		||||
      <div className={styles.InlineDiv}>
 | 
			
		||||
        {sortRadioTypes(Object.keys(radioMap)).map(radio => (
 | 
			
		||||
          <DisabledText
 | 
			
		||||
            key={radio}
 | 
			
		||||
            value={
 | 
			
		||||
              USER_FRIENDLY_BANDWIDTHS[
 | 
			
		||||
                childProfiles?.rf?.[0]?.details?.rfConfigMap?.[radio].channelBandwidth
 | 
			
		||||
              ] ?? 'N/A'
 | 
			
		||||
            }
 | 
			
		||||
            showTooltip={false}
 | 
			
		||||
          />
 | 
			
		||||
        ))}
 | 
			
		||||
      </div>
 | 
			
		||||
    </Item>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  if (errorProfiles) {
 | 
			
		||||
    return (
 | 
			
		||||
      <Alert
 | 
			
		||||
@@ -605,141 +273,13 @@ const General = ({
 | 
			
		||||
      {extraGeneralCards}
 | 
			
		||||
      <Collapse expandIconPosition="right">
 | 
			
		||||
        <Panel header="Advanced Settings" name="settings">
 | 
			
		||||
          {renderItem(' ', data?.details?.radioMap, 'radioType')}
 | 
			
		||||
          <p>Radio Specific Parameters:</p>
 | 
			
		||||
          {renderBandwidthLabels()}
 | 
			
		||||
          {renderItem('Enable Radio', advancedRadioMap, ['radioAdminState'], renderOptionItem, {
 | 
			
		||||
            dropdown: defaultOptions,
 | 
			
		||||
            mapName: 'advancedRadioMap',
 | 
			
		||||
          })}
 | 
			
		||||
          {renderItem(
 | 
			
		||||
            'Deauth Attack Detection',
 | 
			
		||||
            advancedRadioMap,
 | 
			
		||||
            ['deauthAttackDetection'],
 | 
			
		||||
            renderOptionItem,
 | 
			
		||||
            {
 | 
			
		||||
              mapName: 'advancedRadioMap',
 | 
			
		||||
              dropdown: defaultOptionsBoolean,
 | 
			
		||||
            }
 | 
			
		||||
          )}
 | 
			
		||||
          {renderItem('UAPSD', advancedRadioMap, ['uapsdState'], renderOptionItem, {
 | 
			
		||||
            mapName: 'advancedRadioMap',
 | 
			
		||||
            dropdown: defaultOptions,
 | 
			
		||||
          })}
 | 
			
		||||
          {renderChannelItem('Active Channel')}
 | 
			
		||||
          {renderChannelItem('Backup Channel')}
 | 
			
		||||
          {extraFields.map(field =>
 | 
			
		||||
            renderConditionalItem(field.label, field.obj, field.dataIndex, field.dependencies)
 | 
			
		||||
          )}
 | 
			
		||||
          {renderItem(
 | 
			
		||||
            'Management Rate (Mbps)',
 | 
			
		||||
            advancedRadioMap,
 | 
			
		||||
            ['managementRate', 'value'],
 | 
			
		||||
            renderOptionItem,
 | 
			
		||||
            {
 | 
			
		||||
              mapName: 'advancedRadioMap',
 | 
			
		||||
              dropdown: key => (
 | 
			
		||||
                <Select className={styles.Field}>
 | 
			
		||||
                  {key === 'is2dot4GHz' && (
 | 
			
		||||
                    <>
 | 
			
		||||
                      <Option value="rate1mbps">1</Option>
 | 
			
		||||
                      <Option value="rate2mbps">2</Option>
 | 
			
		||||
                      <Option value="rate5dot5mbps">5.5</Option>
 | 
			
		||||
                    </>
 | 
			
		||||
                  )}
 | 
			
		||||
                  <Option value="rate6mbps">6</Option>
 | 
			
		||||
                  <Option value="rate9mbps">9</Option>
 | 
			
		||||
                  {key === 'is2dot4GHz' && <Option value="rate11mbps">11</Option>}
 | 
			
		||||
                  <Option value="rate12mbps">12</Option>
 | 
			
		||||
                  <Option value="rate18mbps">18</Option>
 | 
			
		||||
                  <Option value="rate24mbps">24</Option>
 | 
			
		||||
                </Select>
 | 
			
		||||
              ),
 | 
			
		||||
            }
 | 
			
		||||
          )}
 | 
			
		||||
          {renderItem(
 | 
			
		||||
            'Multicast Rate (Mbps)',
 | 
			
		||||
            advancedRadioMap,
 | 
			
		||||
            ['multicastRate', 'value'],
 | 
			
		||||
            renderOptionItem,
 | 
			
		||||
            {
 | 
			
		||||
              mapName: 'advancedRadioMap',
 | 
			
		||||
              dropdown: (
 | 
			
		||||
                <Select className={styles.Field}>
 | 
			
		||||
                  <Option value="rate6mbps">6</Option>
 | 
			
		||||
                  <Option value="rate9mbps">9</Option>
 | 
			
		||||
                  <Option value="rate12mbps">12</Option>
 | 
			
		||||
                  <Option value="rate18mbps">18</Option>
 | 
			
		||||
                  <Option value="rate24mbps">24</Option>
 | 
			
		||||
                  <Option value="rate36mbps">36</Option>
 | 
			
		||||
                  <Option value="rate48mbps">48</Option>
 | 
			
		||||
                  <Option value="rate54mbps">54</Option>
 | 
			
		||||
                </Select>
 | 
			
		||||
              ),
 | 
			
		||||
            }
 | 
			
		||||
          )}
 | 
			
		||||
          {renderItem(
 | 
			
		||||
            'Probe Response Threshold',
 | 
			
		||||
            radioMap,
 | 
			
		||||
            ['probeResponseThresholdDb', 'value'],
 | 
			
		||||
            renderInputItem,
 | 
			
		||||
            {
 | 
			
		||||
              min: -100,
 | 
			
		||||
              max: -40,
 | 
			
		||||
              error: '-100 - -40 dBm',
 | 
			
		||||
              addOnText: 'dBm',
 | 
			
		||||
              mapName: 'radioMap',
 | 
			
		||||
            }
 | 
			
		||||
          )}
 | 
			
		||||
          {renderItem(
 | 
			
		||||
            'Client Disconnect Threshold',
 | 
			
		||||
            radioMap,
 | 
			
		||||
            ['clientDisconnectThresholdDb', 'value'],
 | 
			
		||||
            renderInputItem,
 | 
			
		||||
            {
 | 
			
		||||
              min: -100,
 | 
			
		||||
              max: 0,
 | 
			
		||||
              error: '-100 - 0 dBm',
 | 
			
		||||
              addOnText: 'dBm',
 | 
			
		||||
              mapName: 'radioMap',
 | 
			
		||||
            }
 | 
			
		||||
          )}
 | 
			
		||||
          {renderItem('EIRP Tx Power', radioMap, ['eirpTxPower', 'value'], renderInputItem, {
 | 
			
		||||
            min: 1,
 | 
			
		||||
            max: 32,
 | 
			
		||||
            error: '1 - 32 dBm',
 | 
			
		||||
            addOnText: 'dBm',
 | 
			
		||||
            mapName: 'radioMap',
 | 
			
		||||
          })}
 | 
			
		||||
 | 
			
		||||
          {renderItem(
 | 
			
		||||
            'SNR',
 | 
			
		||||
            advancedRadioMap,
 | 
			
		||||
            ['bestApSettings', 'value', 'dropInSnrPercentage'],
 | 
			
		||||
            renderInputItem,
 | 
			
		||||
            {
 | 
			
		||||
              min: 0,
 | 
			
		||||
              max: 100,
 | 
			
		||||
              error: '0 - 100%',
 | 
			
		||||
              addOnText: '% Drop',
 | 
			
		||||
              mapName: 'advancedRadioMap',
 | 
			
		||||
              hidden: true,
 | 
			
		||||
            }
 | 
			
		||||
          )}
 | 
			
		||||
          {renderItem(
 | 
			
		||||
            'Min Load',
 | 
			
		||||
            advancedRadioMap,
 | 
			
		||||
            ['bestApSettings', 'value', 'minLoadFactor'],
 | 
			
		||||
            renderInputItem,
 | 
			
		||||
            {
 | 
			
		||||
              min: 0,
 | 
			
		||||
              max: 100,
 | 
			
		||||
              error: '0 - 100%',
 | 
			
		||||
              addOnText: '%',
 | 
			
		||||
              mapName: 'advancedRadioMap',
 | 
			
		||||
              hidden: true,
 | 
			
		||||
            }
 | 
			
		||||
          )}
 | 
			
		||||
          <Advanced
 | 
			
		||||
            extraFields={extraFields}
 | 
			
		||||
            childProfiles={childProfiles}
 | 
			
		||||
            data={data}
 | 
			
		||||
            radioTypes={radioTypes}
 | 
			
		||||
            form={form}
 | 
			
		||||
          />
 | 
			
		||||
        </Panel>
 | 
			
		||||
      </Collapse>
 | 
			
		||||
    </Form>
 | 
			
		||||
 
 | 
			
		||||
@@ -34,87 +34,88 @@
 | 
			
		||||
  :global(.ant-form-item) {
 | 
			
		||||
    flex: 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .InlineBlockDiv {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
    :global(.ant-avatar) {
 | 
			
		||||
      margin-right: 20px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .WifiIcon {
 | 
			
		||||
    background-color: transparent;
 | 
			
		||||
    color: #35a649;
 | 
			
		||||
    border: none;
 | 
			
		||||
    font-size: 40px;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
.InlineBlockDiv {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  vertical-align: middle;
 | 
			
		||||
  :global(.ant-avatar) {
 | 
			
		||||
    margin-right: 20px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .backButton {
 | 
			
		||||
    margin-top: 5px;
 | 
			
		||||
  }
 | 
			
		||||
.WifiIcon {
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  color: #35a649;
 | 
			
		||||
  border: none;
 | 
			
		||||
  font-size: 40px;
 | 
			
		||||
  vertical-align: middle;
 | 
			
		||||
  margin-right: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .InlineDiv {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-wrap: wrap;
 | 
			
		||||
    justify-content: space-around;
 | 
			
		||||
  }
 | 
			
		||||
.backButton {
 | 
			
		||||
  margin-top: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .InlineDiv > * {
 | 
			
		||||
    margin: 0 10px 0 0;
 | 
			
		||||
  }
 | 
			
		||||
.InlineDiv {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
  justify-content: space-around;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .InlineBetweenDiv {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: space-between;
 | 
			
		||||
  }
 | 
			
		||||
.InlineDiv > * {
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  margin: 0 10px 0 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .saveButton {
 | 
			
		||||
    margin-top: 10px;
 | 
			
		||||
    min-width: 139px;
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
  }
 | 
			
		||||
.InlineBetweenDiv {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .InlineEndDiv {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: flex-end;
 | 
			
		||||
  }
 | 
			
		||||
.saveButton {
 | 
			
		||||
  margin-top: 10px;
 | 
			
		||||
  min-width: 139px;
 | 
			
		||||
  max-width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  p {
 | 
			
		||||
    font-weight: bold !important;
 | 
			
		||||
    font-size: 16px;
 | 
			
		||||
  }
 | 
			
		||||
  .spanStyle {
 | 
			
		||||
    flex: 1;
 | 
			
		||||
    margin-left: 10px;
 | 
			
		||||
  }
 | 
			
		||||
.InlineEndDiv {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: flex-end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .UpgradeState {
 | 
			
		||||
    margin-left: 20px;
 | 
			
		||||
  }
 | 
			
		||||
p {
 | 
			
		||||
  font-weight: bold !important;
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
}
 | 
			
		||||
.spanStyle {
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  margin-left: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .HeaderDiv {
 | 
			
		||||
    display: flex;
 | 
			
		||||
  }
 | 
			
		||||
.UpgradeState {
 | 
			
		||||
  margin-left: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .troubleshootBtnsDiv {
 | 
			
		||||
    & > * {
 | 
			
		||||
      margin-right: 10px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
.HeaderDiv {
 | 
			
		||||
  display: flex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .tabContentContainer {
 | 
			
		||||
    margin-top: 10px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .Row {
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  a {
 | 
			
		||||
    color: white;
 | 
			
		||||
.troubleshootBtnsDiv {
 | 
			
		||||
  & > * {
 | 
			
		||||
    margin-right: 10px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tabContentContainer {
 | 
			
		||||
  margin-top: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.Row {
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a {
 | 
			
		||||
  color: white;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,8 @@ export { default as BlockedList } from 'containers/BlockedList';
 | 
			
		||||
 | 
			
		||||
export { default as ClientDeviceDetails } from 'containers/ClientDeviceDetails';
 | 
			
		||||
export { default as NetworkTableContainer } from 'containers/NetworkTableContainer';
 | 
			
		||||
export { default as APFirmware } from 'containers/AccessPointDetails/components/Firmware';
 | 
			
		||||
export { default as APAdvanced } from 'containers/AccessPointDetails/components/General/components/Advanced';
 | 
			
		||||
 | 
			
		||||
export { default as ThemeProvider } from 'contexts/ThemeProvider';
 | 
			
		||||
export { default as RolesProvider } from 'contexts/RolesProvider';
 | 
			
		||||
@@ -42,13 +44,13 @@ export { default as ScrollToTop } from 'components/ScrollToTop';
 | 
			
		||||
export { default as LineGraphTooltip } from 'components/GraphTooltips/LineGraphTooltip';
 | 
			
		||||
export { default as PieGraphTooltip } from 'components/GraphTooltips/PieGraphTooltip';
 | 
			
		||||
export { default as Skeleton } from 'components/Skeleton';
 | 
			
		||||
export { default as Timer } from 'components/Timer';
 | 
			
		||||
export {
 | 
			
		||||
  Table as SkeletonTable,
 | 
			
		||||
  List as SkeletonList,
 | 
			
		||||
  Card as SkeletonCard,
 | 
			
		||||
} from 'components/Skeleton';
 | 
			
		||||
export { default as DisabledText } from 'components/DisabledText';
 | 
			
		||||
export { default as Timer } from 'components/Timer';
 | 
			
		||||
 | 
			
		||||
export { default as WithRoles } from 'components/WithRoles';
 | 
			
		||||
export { Input, Select, Switch, RoleProtectedBtn } from 'components/WithRoles';
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user