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:
Sean Macfarlane
2021-11-03 14:39:57 -04:00
committed by GitHub
4 changed files with 590 additions and 540 deletions

View File

@@ -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;

View File

@@ -11,16 +11,14 @@ import {
Empty, Empty,
Typography, Typography,
} from 'antd'; } from 'antd';
import _ from 'lodash';
import { Card } from 'components/Skeleton'; import { Card } from 'components/Skeleton';
import { Input, Select, RoleProtectedBtn } from 'components/WithRoles'; import { Input, Select, RoleProtectedBtn } from 'components/WithRoles';
import _ from 'lodash';
import ThemeContext from 'contexts/ThemeContext'; 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 { pageLayout } from 'utils/form';
import { USER_FRIENDLY_RATES, USER_FRIENDLY_BANDWIDTHS } from './constants';
import Advanced from './components/Advanced';
import styles from '../../index.module.scss'; import styles from '../../index.module.scss';
@@ -112,80 +110,8 @@ const General = ({
longitude, longitude,
serial, serial,
lastModifiedTimestamp, lastModifiedTimestamp,
details: { advancedRadioMap = {}, radioMap = {} } = {},
} = data; } = 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 = () => { const handleOnSave = () => {
form form
.validateFields() .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) { if (errorProfiles) {
return ( return (
<Alert <Alert
@@ -605,141 +273,13 @@ const General = ({
{extraGeneralCards} {extraGeneralCards}
<Collapse expandIconPosition="right"> <Collapse expandIconPosition="right">
<Panel header="Advanced Settings" name="settings"> <Panel header="Advanced Settings" name="settings">
{renderItem(' ', data?.details?.radioMap, 'radioType')} <Advanced
<p>Radio Specific Parameters:</p> extraFields={extraFields}
{renderBandwidthLabels()} childProfiles={childProfiles}
{renderItem('Enable Radio', advancedRadioMap, ['radioAdminState'], renderOptionItem, { data={data}
dropdown: defaultOptions, radioTypes={radioTypes}
mapName: 'advancedRadioMap', form={form}
})} />
{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,
}
)}
</Panel> </Panel>
</Collapse> </Collapse>
</Form> </Form>

View File

@@ -34,87 +34,88 @@
:global(.ant-form-item) { :global(.ant-form-item) {
flex: 1; flex: 1;
} }
}
.InlineBlockDiv { .InlineBlockDiv {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
:global(.ant-avatar) { :global(.ant-avatar) {
margin-right: 20px;
}
}
.WifiIcon {
background-color: transparent;
color: #35a649;
border: none;
font-size: 40px;
vertical-align: middle;
margin-right: 20px; margin-right: 20px;
} }
}
.backButton { .WifiIcon {
margin-top: 5px; background-color: transparent;
} color: #35a649;
border: none;
font-size: 40px;
vertical-align: middle;
margin-right: 20px;
}
.InlineDiv { .backButton {
display: flex; margin-top: 5px;
flex-wrap: wrap; }
justify-content: space-around;
}
.InlineDiv > * { .InlineDiv {
margin: 0 10px 0 0; display: flex;
} flex-wrap: wrap;
justify-content: space-around;
}
.InlineBetweenDiv { .InlineDiv > * {
display: flex; flex: 1;
align-items: center; margin: 0 10px 0 0;
justify-content: space-between; }
}
.saveButton { .InlineBetweenDiv {
margin-top: 10px; display: flex;
min-width: 139px; align-items: center;
max-width: 100%; justify-content: space-between;
} }
.InlineEndDiv { .saveButton {
display: flex; margin-top: 10px;
justify-content: flex-end; min-width: 139px;
} max-width: 100%;
}
p { .InlineEndDiv {
font-weight: bold !important; display: flex;
font-size: 16px; justify-content: flex-end;
} }
.spanStyle {
flex: 1;
margin-left: 10px;
}
.UpgradeState { p {
margin-left: 20px; font-weight: bold !important;
} font-size: 16px;
}
.spanStyle {
flex: 1;
margin-left: 10px;
}
.HeaderDiv { .UpgradeState {
display: flex; margin-left: 20px;
} }
.troubleshootBtnsDiv { .HeaderDiv {
& > * { display: flex;
margin-right: 10px; }
}
}
.tabContentContainer { .troubleshootBtnsDiv {
margin-top: 10px; & > * {
} margin-right: 10px;
.Row {
cursor: pointer;
}
a {
color: white;
} }
} }
.tabContentContainer {
margin-top: 10px;
}
.Row {
cursor: pointer;
}
a {
color: white;
}

View File

@@ -19,6 +19,8 @@ export { default as BlockedList } from 'containers/BlockedList';
export { default as ClientDeviceDetails } from 'containers/ClientDeviceDetails'; export { default as ClientDeviceDetails } from 'containers/ClientDeviceDetails';
export { default as NetworkTableContainer } from 'containers/NetworkTableContainer'; 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 ThemeProvider } from 'contexts/ThemeProvider';
export { default as RolesProvider } from 'contexts/RolesProvider'; 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 LineGraphTooltip } from 'components/GraphTooltips/LineGraphTooltip';
export { default as PieGraphTooltip } from 'components/GraphTooltips/PieGraphTooltip'; export { default as PieGraphTooltip } from 'components/GraphTooltips/PieGraphTooltip';
export { default as Skeleton } from 'components/Skeleton'; export { default as Skeleton } from 'components/Skeleton';
export { default as Timer } from 'components/Timer';
export { export {
Table as SkeletonTable, Table as SkeletonTable,
List as SkeletonList, List as SkeletonList,
Card as SkeletonCard, Card as SkeletonCard,
} from 'components/Skeleton'; } from 'components/Skeleton';
export { default as DisabledText } from 'components/DisabledText'; export { default as DisabledText } from 'components/DisabledText';
export { default as Timer } from 'components/Timer';
export { default as WithRoles } from 'components/WithRoles'; export { default as WithRoles } from 'components/WithRoles';
export { Input, Select, Switch, RoleProtectedBtn } from 'components/WithRoles'; export { Input, Select, Switch, RoleProtectedBtn } from 'components/WithRoles';