mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentral-ui-libs.git
synced 2025-11-03 04:08:05 +00:00
Version 1.0.26
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ucentral-libs",
|
"name": "ucentral-libs",
|
||||||
"version": "1.0.1",
|
"version": "1.0.26",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ucentral-libs",
|
"name": "ucentral-libs",
|
||||||
"version": "1.0.1",
|
"version": "1.0.26",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@coreui/coreui": "^3.4.0",
|
"@coreui/coreui": "^3.4.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ucentral-libs",
|
"name": "ucentral-libs",
|
||||||
"version": "1.0.1",
|
"version": "1.0.26",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"source": "src/index.js",
|
"source": "src/index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ const AddContactForm = ({ t, disable, fields, updateField, updateFieldWithKey, e
|
|||||||
<CInvalidFeedback>{t('common.required')}</CInvalidFeedback>
|
<CInvalidFeedback>{t('common.required')}</CInvalidFeedback>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
||||||
Landlines
|
{t('location.phones')}
|
||||||
</CLabel>
|
</CLabel>
|
||||||
<CCol sm="4">
|
<CCol sm="4">
|
||||||
<CreatableSelect
|
<CreatableSelect
|
||||||
@@ -267,11 +267,11 @@ const AddContactForm = ({ t, disable, fields, updateField, updateFieldWithKey, e
|
|||||||
components={{ NoOptionsMessage }}
|
components={{ NoOptionsMessage }}
|
||||||
options={[]}
|
options={[]}
|
||||||
value={fields.phones.value.map((opt) => ({ value: opt, label: opt }))}
|
value={fields.phones.value.map((opt) => ({ value: opt, label: opt }))}
|
||||||
placeholder={t('common.type_for_options')}
|
placeholder="+1(202)555-0103"
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
||||||
Mobiles
|
{t('location.mobiles')}
|
||||||
</CLabel>
|
</CLabel>
|
||||||
<CCol sm="4">
|
<CCol sm="4">
|
||||||
<CreatableSelect
|
<CreatableSelect
|
||||||
@@ -282,7 +282,7 @@ const AddContactForm = ({ t, disable, fields, updateField, updateFieldWithKey, e
|
|||||||
components={{ NoOptionsMessage }}
|
components={{ NoOptionsMessage }}
|
||||||
options={[]}
|
options={[]}
|
||||||
value={fields.mobiles.value.map((opt) => ({ value: opt, label: opt }))}
|
value={fields.mobiles.value.map((opt) => ({ value: opt, label: opt }))}
|
||||||
placeholder={t('common.type_for_options')}
|
placeholder="+1(202)555-0103"
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CLabel sm="2" col htmlFor="description">
|
<CLabel sm="2" col htmlFor="description">
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ const AddLocationForm = ({
|
|||||||
</div>
|
</div>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
||||||
Landlines
|
{t('location.phones')}
|
||||||
</CLabel>
|
</CLabel>
|
||||||
<CCol sm="4">
|
<CCol sm="4">
|
||||||
<CreatableSelect
|
<CreatableSelect
|
||||||
@@ -141,11 +141,11 @@ const AddLocationForm = ({
|
|||||||
components={{ NoOptionsMessage }}
|
components={{ NoOptionsMessage }}
|
||||||
options={[]}
|
options={[]}
|
||||||
value={fields.phones.value.map((opt) => ({ value: opt, label: opt }))}
|
value={fields.phones.value.map((opt) => ({ value: opt, label: opt }))}
|
||||||
placeholder={t('common.type_for_options')}
|
placeholder="+1(202)555-0103"
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
||||||
Mobiles
|
{t('location.mobiles')}
|
||||||
</CLabel>
|
</CLabel>
|
||||||
<CCol sm="4">
|
<CCol sm="4">
|
||||||
<CreatableSelect
|
<CreatableSelect
|
||||||
@@ -156,7 +156,7 @@ const AddLocationForm = ({
|
|||||||
components={{ NoOptionsMessage }}
|
components={{ NoOptionsMessage }}
|
||||||
options={[]}
|
options={[]}
|
||||||
value={fields.mobiles.value.map((opt) => ({ value: opt, label: opt }))}
|
value={fields.mobiles.value.map((opt) => ({ value: opt, label: opt }))}
|
||||||
placeholder={t('common.type_for_options')}
|
placeholder="+1(202)555-0103"
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol className="mb-3" sm="12">
|
<CCol className="mb-3" sm="12">
|
||||||
|
|||||||
542
src/components/AddSimulationForm/index.js
Normal file
542
src/components/AddSimulationForm/index.js
Normal file
@@ -0,0 +1,542 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {
|
||||||
|
CForm,
|
||||||
|
CInput,
|
||||||
|
CLabel,
|
||||||
|
CCol,
|
||||||
|
CFormText,
|
||||||
|
CRow,
|
||||||
|
CInputFile,
|
||||||
|
CInvalidFeedback,
|
||||||
|
CSelect,
|
||||||
|
} from '@coreui/react';
|
||||||
|
import RequiredAsterisk from '../RequiredAsterisk';
|
||||||
|
|
||||||
|
const validatePem = (value) =>
|
||||||
|
(value.includes('---BEGIN CERTIFICATE---') && value.includes('---END CERTIFICATE---')) ||
|
||||||
|
(value.includes('---BEGIN PRIVATE KEY---') && value.includes('---END PRIVATE KEY---'));
|
||||||
|
|
||||||
|
const AddSimulationForm = ({ t, show, disable, fields, updateField, updateFieldWithKey }) => {
|
||||||
|
const [certKey, setCertKey] = useState(0);
|
||||||
|
const [keyKey, setKeyKey] = useState(0);
|
||||||
|
|
||||||
|
let fileReader;
|
||||||
|
|
||||||
|
const handleCertFileRead = () => {
|
||||||
|
updateFieldWithKey('certificate', { error: false });
|
||||||
|
const content = fileReader.result;
|
||||||
|
if (content && validatePem(content)) {
|
||||||
|
updateFieldWithKey('certificate', { value: content });
|
||||||
|
} else {
|
||||||
|
updateFieldWithKey('certificate', { error: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCertFile = (file) => {
|
||||||
|
fileReader = new FileReader();
|
||||||
|
fileReader.onloadend = handleCertFileRead;
|
||||||
|
fileReader.readAsText(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyFileRead = () => {
|
||||||
|
updateFieldWithKey('key', { error: false });
|
||||||
|
const content = fileReader.result;
|
||||||
|
if (content && validatePem(content)) {
|
||||||
|
updateFieldWithKey('key', { value: content });
|
||||||
|
} else {
|
||||||
|
updateFieldWithKey('key', { error: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyFile = (file) => {
|
||||||
|
fileReader = new FileReader();
|
||||||
|
fileReader.onloadend = handleKeyFileRead;
|
||||||
|
fileReader.readAsText(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (show) {
|
||||||
|
setCertKey(certKey + 1);
|
||||||
|
setKeyKey(keyKey + 1);
|
||||||
|
}
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CForm>
|
||||||
|
<CRow>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="name">
|
||||||
|
{t('user.name')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="name"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={fields.name.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={fields.name.error}
|
||||||
|
disabled={disable}
|
||||||
|
maxLength="50"
|
||||||
|
/>
|
||||||
|
<CFormText hidden={!fields.name.error} color={fields.name.error ? 'danger' : ''}>
|
||||||
|
{t('common.required')}
|
||||||
|
</CFormText>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="gateway">
|
||||||
|
{t('simulation.gateway')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="gateway"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={fields.gateway.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={fields.gateway.error}
|
||||||
|
disabled={disable}
|
||||||
|
maxLength="50"
|
||||||
|
/>
|
||||||
|
<CFormText hidden={!fields.gateway.error} color={fields.gateway.error ? 'danger' : ''}>
|
||||||
|
{t('common.required')}
|
||||||
|
</CFormText>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="certificate">
|
||||||
|
{t('common.certificate')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInputFile
|
||||||
|
className="mt-1"
|
||||||
|
key={certKey}
|
||||||
|
id="file-input"
|
||||||
|
name="file-input"
|
||||||
|
accept=".pem"
|
||||||
|
onChange={(e) => handleCertFile(e.target.files[0])}
|
||||||
|
/>
|
||||||
|
<CFormText
|
||||||
|
hidden={!fields.certificate.error}
|
||||||
|
color={fields.certificate.error ? 'danger' : ''}
|
||||||
|
>
|
||||||
|
{t('common.required')}
|
||||||
|
</CFormText>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="key">
|
||||||
|
{t('common.key')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInputFile
|
||||||
|
className="mt-1"
|
||||||
|
key={keyKey}
|
||||||
|
id="file-input"
|
||||||
|
name="file-input"
|
||||||
|
accept=".pem"
|
||||||
|
onChange={(e) => handleKeyFile(e.target.files[0])}
|
||||||
|
/>
|
||||||
|
<CFormText hidden={!fields.key.error} color={fields.key.error ? 'danger' : ''}>
|
||||||
|
{t('common.required')}
|
||||||
|
</CFormText>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="macPrefix">
|
||||||
|
{t('simulation.mac_prefix')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="macPrefix"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={fields.macPrefix.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={fields.macPrefix.error}
|
||||||
|
disabled={disable}
|
||||||
|
maxLength="50"
|
||||||
|
/>
|
||||||
|
<CFormText
|
||||||
|
hidden={!fields.macPrefix.error}
|
||||||
|
color={fields.macPrefix.error ? 'danger' : ''}
|
||||||
|
>
|
||||||
|
{t('simulation.prefix_length')}
|
||||||
|
</CFormText>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="devices">
|
||||||
|
{t('common.devices')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="devices"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.devices.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.devices.value < fields.devices.min || fields.devices.value > fields.devices.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.devices.min,
|
||||||
|
max: fields.devices.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="healthCheckInterval">
|
||||||
|
{t('simulation.healtcheck_interval')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="healthCheckInterval"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.healthCheckInterval.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.healthCheckInterval.value < fields.healthCheckInterval.min ||
|
||||||
|
fields.healthCheckInterval.value > fields.healthCheckInterval.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.healthCheckInterval.min,
|
||||||
|
max: fields.healthCheckInterval.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="stateInterval">
|
||||||
|
{t('simulation.state_interval')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="stateInterval"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.stateInterval.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.stateInterval.value < fields.stateInterval.min ||
|
||||||
|
fields.stateInterval.value > fields.stateInterval.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.stateInterval.min,
|
||||||
|
max: fields.stateInterval.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="clientInterval">
|
||||||
|
{t('simulation.client_interval')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="clientInterval"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.clientInterval.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.clientInterval.value < fields.clientInterval.min ||
|
||||||
|
fields.clientInterval.value > fields.clientInterval.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.clientInterval.min,
|
||||||
|
max: fields.clientInterval.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="reconnectInterval">
|
||||||
|
{t('simulation.reconnect_interval')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="reconnectInterval"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.reconnectInterval.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.reconnectInterval.value < fields.reconnectInterval.min ||
|
||||||
|
fields.reconnectInterval.value > fields.reconnectInterval.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.reconnectInterval.min,
|
||||||
|
max: fields.reconnectInterval.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="minAssociations">
|
||||||
|
{t('simulation.min_associations')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="minAssociations"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.minAssociations.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.minAssociations.value < fields.minAssociations.min ||
|
||||||
|
fields.minAssociations.value > fields.minAssociations.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.minAssociations.min,
|
||||||
|
max: fields.minAssociations.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="maxAssociations">
|
||||||
|
{t('simulation.max_associations')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="maxAssociations"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.maxAssociations.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.maxAssociations.value < fields.maxAssociations.min ||
|
||||||
|
fields.maxAssociations.value > fields.maxAssociations.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.maxAssociations.min,
|
||||||
|
max: fields.maxAssociations.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="minClients">
|
||||||
|
{t('simulation.min_clients')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="minClients"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.minClients.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.minClients.value < fields.minClients.min ||
|
||||||
|
fields.minClients.value > fields.minClients.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.minClients.min,
|
||||||
|
max: fields.minClients.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="maxClients">
|
||||||
|
{t('simulation.max_clients')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="maxClients"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.maxClients.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.maxClients.value < fields.maxClients.min ||
|
||||||
|
fields.maxClients.value > fields.maxClients.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.maxClients.min,
|
||||||
|
max: fields.maxClients.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="simulationLength">
|
||||||
|
{t('simulation.length')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="simulationLength"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.simulationLength.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.simulationLength.value < fields.simulationLength.min ||
|
||||||
|
fields.simulationLength.value > fields.simulationLength.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.simulationLength.min,
|
||||||
|
max: fields.simulationLength.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="threads">
|
||||||
|
{t('simulation.threads')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="threads"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.threads.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.threads.value < fields.threads.min || fields.threads.value > fields.threads.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.threads.min,
|
||||||
|
max: fields.threads.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="keepAlive">
|
||||||
|
{t('simulation.keep_alive')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="keepAlive"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.keepAlive.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.keepAlive.value < fields.keepAlive.min ||
|
||||||
|
fields.keepAlive.value > fields.keepAlive.max
|
||||||
|
}
|
||||||
|
disabled={disable}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.keepAlive.min,
|
||||||
|
max: fields.keepAlive.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="deviceType">
|
||||||
|
{t('configuration.device_type')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CSelect
|
||||||
|
custom
|
||||||
|
id="deviceType"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={fields.deviceType.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={fields.deviceType.error}
|
||||||
|
disabled={disable}
|
||||||
|
maxLength="50"
|
||||||
|
>
|
||||||
|
<option value="cig_wf160d">cig_wf160d</option>
|
||||||
|
<option value="cig_wf188">cig_wf188</option>
|
||||||
|
<option value="cig_wf188n">cig_wf188n</option>
|
||||||
|
<option value="cig_wf194c">cig_wf194c</option>
|
||||||
|
<option value="cig_wf194c4">cig_wf194c4</option>
|
||||||
|
<option value="edgecore_eap101">edgecore_eap101</option>
|
||||||
|
<option value="edgecore_eap102">edgecore_eap102</option>
|
||||||
|
<option value="edgecore_ecs4100-12ph">edgecore_ecs4100-12ph</option>
|
||||||
|
<option value="edgecore_ecw5211">edgecore_ecw5211</option>
|
||||||
|
<option value="edgecore_ecw5410">edgecore_ecw5410</option>
|
||||||
|
<option value="edgecore_oap100">edgecore_oap100</option>
|
||||||
|
<option value="edgecore_spw2ac1200">edgecore_spw2ac1200</option>
|
||||||
|
<option value="edgecore_spw2ac1200-lan-poe">edgecore_spw2ac1200-lan-poe</option>
|
||||||
|
<option value="edgecore_ssw2ac2600">edgecore_ssw2ac2600</option>
|
||||||
|
<option value="hfcl_ion4.yml">hfcl_ion4.yml</option>
|
||||||
|
<option value="indio_um-305ac">indio_um-305ac</option>
|
||||||
|
<option value="linksys_e8450-ubi">linksys_e8450-ubi</option>
|
||||||
|
<option value="linksys_ea6350">linksys_ea6350</option>
|
||||||
|
<option value="linksys_ea6350-v4">linksys_ea6350-v4</option>
|
||||||
|
<option value="linksys_ea8300">linksys_ea8300</option>
|
||||||
|
<option value="mikrotik_nand">mikrotik_nand</option>
|
||||||
|
<option value="tp-link_ec420-g1">tp-link_ec420-g1</option>
|
||||||
|
<option value="tplink_cpe210_v3">tplink_cpe210_v3</option>
|
||||||
|
<option value="tplink_cpe510_v3">tplink_cpe510_v3</option>
|
||||||
|
<option value="tplink_eap225_outdoor_v1">tplink_eap225_outdoor_v1</option>
|
||||||
|
<option value="tplink_ec420">tplink_ec420</option>
|
||||||
|
<option value="tplink_ex227">tplink_ex227</option>
|
||||||
|
<option value="tplink_ex228">tplink_ex228</option>
|
||||||
|
<option value="tplink_ex447">tplink_ex447</option>
|
||||||
|
<option value="wallys_dr40x9">wallys_dr40x9</option>
|
||||||
|
</CSelect>
|
||||||
|
<CFormText
|
||||||
|
hidden={!fields.deviceType.error}
|
||||||
|
color={fields.deviceType.error ? 'danger' : ''}
|
||||||
|
>
|
||||||
|
{t('common.required')}
|
||||||
|
</CFormText>
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
</CForm>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
AddSimulationForm.propTypes = {
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
|
show: PropTypes.bool.isRequired,
|
||||||
|
disable: PropTypes.bool.isRequired,
|
||||||
|
fields: PropTypes.instanceOf(Object).isRequired,
|
||||||
|
updateField: PropTypes.func.isRequired,
|
||||||
|
updateFieldWithKey: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddSimulationForm;
|
||||||
@@ -6,7 +6,7 @@ import _ from 'lodash';
|
|||||||
import formatGoogleAddress from 'utils/formatGoogleAddress';
|
import formatGoogleAddress from 'utils/formatGoogleAddress';
|
||||||
import selectStyles from 'utils/selectStyles';
|
import selectStyles from 'utils/selectStyles';
|
||||||
|
|
||||||
const AddressEditor = ({ t, currentToken, endpoint, setAddress, show }) => {
|
const AddressEditor = ({ t, currentToken, endpoint, setAddress, show, disabled }) => {
|
||||||
const [tempValue, setTempValue] = useState('');
|
const [tempValue, setTempValue] = useState('');
|
||||||
const [socket, setSocket] = useState(null);
|
const [socket, setSocket] = useState(null);
|
||||||
const [results, setResults] = useState([]);
|
const [results, setResults] = useState([]);
|
||||||
@@ -25,7 +25,7 @@ const AddressEditor = ({ t, currentToken, endpoint, setAddress, show }) => {
|
|||||||
|
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(v) => {
|
(v) => {
|
||||||
setWaitingSearch(v);
|
if (v.length >= 4) setWaitingSearch(v);
|
||||||
},
|
},
|
||||||
[setWaitingSearch],
|
[setWaitingSearch],
|
||||||
);
|
);
|
||||||
@@ -115,6 +115,7 @@ const AddressEditor = ({ t, currentToken, endpoint, setAddress, show }) => {
|
|||||||
placeholder={t('location.search')}
|
placeholder={t('location.search')}
|
||||||
onInputChange={handleTyping}
|
onInputChange={handleTyping}
|
||||||
onChange={(property) => changeAddress(property)}
|
onChange={(property) => changeAddress(property)}
|
||||||
|
isDisabled={disabled}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -125,6 +126,11 @@ AddressEditor.propTypes = {
|
|||||||
endpoint: PropTypes.string.isRequired,
|
endpoint: PropTypes.string.isRequired,
|
||||||
setAddress: PropTypes.func.isRequired,
|
setAddress: PropTypes.func.isRequired,
|
||||||
show: PropTypes.bool.isRequired,
|
show: PropTypes.bool.isRequired,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressEditor.defaultProps = {
|
||||||
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddressEditor;
|
export default AddressEditor;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ const CustomMultiModal = ({
|
|||||||
noTable,
|
noTable,
|
||||||
toggleAdd,
|
toggleAdd,
|
||||||
reset,
|
reset,
|
||||||
|
disabled,
|
||||||
}) => {
|
}) => {
|
||||||
const [show, toggle] = useToggle();
|
const [show, toggle] = useToggle();
|
||||||
|
|
||||||
@@ -82,12 +83,19 @@ const CustomMultiModal = ({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
className="ml-2"
|
className="ml-2"
|
||||||
onClick={toggleAdd}
|
onClick={toggleAdd}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<CIcon content={cilPlus} />
|
<CIcon content={cilPlus} />
|
||||||
</CButton>
|
</CButton>
|
||||||
</CPopover>
|
</CPopover>
|
||||||
<CPopover content={t('common.save')}>
|
<CPopover content={t('common.save')}>
|
||||||
<CButton color="primary" variant="outline" className="ml-2" onClick={closeAndSave}>
|
<CButton
|
||||||
|
color="primary"
|
||||||
|
variant="outline"
|
||||||
|
className="ml-2"
|
||||||
|
onClick={closeAndSave}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
<CIcon content={cilSave} />
|
<CIcon content={cilSave} />
|
||||||
</CButton>
|
</CButton>
|
||||||
</CPopover>
|
</CPopover>
|
||||||
@@ -145,6 +153,7 @@ CustomMultiModal.propTypes = {
|
|||||||
noTable: PropTypes.bool,
|
noTable: PropTypes.bool,
|
||||||
toggleAdd: PropTypes.func,
|
toggleAdd: PropTypes.func,
|
||||||
reset: PropTypes.func,
|
reset: PropTypes.func,
|
||||||
|
disabled: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
CustomMultiModal.defaultProps = {
|
CustomMultiModal.defaultProps = {
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CButtonClose } from '@coreui/react';
|
import { CButtonClose } from '@coreui/react';
|
||||||
|
|
||||||
const SectionToggler = ({ id, label, field, updateField }) => {
|
const SectionToggler = ({ id, label, field, updateField, disabled }) => {
|
||||||
const toggle = () => updateField(id, { enabled: !field.enabled });
|
const toggle = () => updateField(id, { enabled: !field.enabled });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-1 pb-0 mb-0">
|
<div className="py-1 pb-0 mb-0">
|
||||||
<h6 className="mt-1 float-left">{label}</h6>
|
<h6 className="mt-1 float-left">{label}</h6>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<CButtonClose onClick={toggle} style={{ color: 'white' }} />
|
<CButtonClose onClick={toggle} style={{ color: 'white' }} disabled={disabled} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -20,6 +20,7 @@ SectionToggler.propTypes = {
|
|||||||
field: PropTypes.instanceOf(Object).isRequired,
|
field: PropTypes.instanceOf(Object).isRequired,
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
updateField: PropTypes.func.isRequired,
|
updateField: PropTypes.func.isRequired,
|
||||||
|
disabled: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SectionToggler;
|
export default SectionToggler;
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ const ConfigurationTable = ({
|
|||||||
deleteConfig={deleteConfig}
|
deleteConfig={deleteConfig}
|
||||||
hideTooltips={hideTooltips}
|
hideTooltips={hideTooltips}
|
||||||
/>
|
/>
|
||||||
<CPopover content={t('configuration.view_config')}>
|
<CPopover content={t('common.details')}>
|
||||||
<CButton
|
<CButton
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
64
src/components/ConfirmStopEditingButton/index.js
Normal file
64
src/components/ConfirmStopEditingButton/index.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {
|
||||||
|
CAlert,
|
||||||
|
CButton,
|
||||||
|
CModal,
|
||||||
|
CModalBody,
|
||||||
|
CModalHeader,
|
||||||
|
CModalTitle,
|
||||||
|
CPopover,
|
||||||
|
} from '@coreui/react';
|
||||||
|
import CIcon from '@coreui/icons-react';
|
||||||
|
import { cilX } from '@coreui/icons';
|
||||||
|
import useToggle from '../../hooks/useToggle';
|
||||||
|
|
||||||
|
const ConfirmStopEditingButton = ({ t, stopEditing, disabled }) => {
|
||||||
|
const [show, toggleShow] = useToggle();
|
||||||
|
|
||||||
|
const toggleAndStop = () => {
|
||||||
|
toggleShow();
|
||||||
|
stopEditing();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CPopover content={t('common.stop_editing')}>
|
||||||
|
<CButton disabled={disabled} color="dark" onClick={toggleShow} className="ml-2">
|
||||||
|
<CIcon name="cil-x" content={cilX} />
|
||||||
|
</CButton>
|
||||||
|
</CPopover>
|
||||||
|
<CModal show={show} onClose={toggleShow}>
|
||||||
|
<CModalHeader className="p-1">
|
||||||
|
<CModalTitle className="pl-1 pt-1 text-dark">{t('common.stop_editing')}</CModalTitle>
|
||||||
|
<div className="text-right">
|
||||||
|
<CPopover content={t('common.close')}>
|
||||||
|
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleShow}>
|
||||||
|
<CIcon content={cilX} />
|
||||||
|
</CButton>
|
||||||
|
</CPopover>
|
||||||
|
</div>
|
||||||
|
</CModalHeader>
|
||||||
|
<CModalBody>
|
||||||
|
<CAlert color="danger">{t('common.confirm_stop_editing')}</CAlert>
|
||||||
|
<div className="text-center">
|
||||||
|
<CButton className="mr-2" color="danger" onClick={toggleAndStop}>
|
||||||
|
{t('common.stop_editing')}
|
||||||
|
</CButton>
|
||||||
|
<CButton className="ml-2" color="light" onClick={toggleShow}>
|
||||||
|
{t('common.go_back')}
|
||||||
|
</CButton>
|
||||||
|
</div>
|
||||||
|
</CModalBody>
|
||||||
|
</CModal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfirmStopEditingButton.propTypes = {
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
|
stopEditing: PropTypes.func.isRequired,
|
||||||
|
disabled: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmStopEditingButton;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CButton, CDataTable, CLink, CPopover, CButtonToolbar } from '@coreui/react';
|
import { CButton, CDataTable, CLink, CPopover, CButtonToolbar } from '@coreui/react';
|
||||||
import { cilPencil, cilPlus } from '@coreui/icons';
|
import { cilMagnifyingGlass, cilPlus } from '@coreui/icons';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
import ReactTooltip from 'react-tooltip';
|
import ReactTooltip from 'react-tooltip';
|
||||||
import DeleteButton from './DeleteButton';
|
import DeleteButton from './DeleteButton';
|
||||||
@@ -118,7 +118,7 @@ const ContactTable = ({
|
|||||||
deleteContact={deleteContact}
|
deleteContact={deleteContact}
|
||||||
hideTooltips={hideTooltips}
|
hideTooltips={hideTooltips}
|
||||||
/>
|
/>
|
||||||
<CPopover content={t('common.edit')}>
|
<CPopover content={t('common.details')}>
|
||||||
<CButton
|
<CButton
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -128,7 +128,7 @@ const ContactTable = ({
|
|||||||
onClick={() => toggleEditModal(item.id)}
|
onClick={() => toggleEditModal(item.id)}
|
||||||
style={{ width: '33px', height: '30px' }}
|
style={{ width: '33px', height: '30px' }}
|
||||||
>
|
>
|
||||||
<CIcon name="cil-pencil" content={cilPencil} size="sm" />
|
<CIcon name="cil-magnifying-glass" content={cilMagnifyingGlass} size="sm" />
|
||||||
</CButton>
|
</CButton>
|
||||||
</CPopover>
|
</CPopover>
|
||||||
</CButtonToolbar>
|
</CButtonToolbar>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const CopyToClipboardButton = ({ t, content, size }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<CPopover content={t('common.copy_to_clipboard')}>
|
<CPopover content={t('common.copy_to_clipboard')}>
|
||||||
<CButton onClick={copyToClipboard} size={size}>
|
<CButton onClick={copyToClipboard} size={size} className="py-0">
|
||||||
<CIcon content={cilClone} />
|
<CIcon content={cilClone} />
|
||||||
{' '}
|
{' '}
|
||||||
{result || ''}
|
{result || ''}
|
||||||
|
|||||||
@@ -77,10 +77,11 @@ const DeviceDetails = ({ t, loading, getData, status, deviceConfig, lastStats })
|
|||||||
</div>
|
</div>
|
||||||
<CRow>
|
<CRow>
|
||||||
<CCol lg="2" xl="1" xxl="1">
|
<CCol lg="2" xl="1" xxl="1">
|
||||||
<CLabel>{t('common.serial_number')}: </CLabel>
|
<CLabel>{t('common.serial_num')}: </CLabel>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol className="border-right" lg="2" xl="3" xxl="3">
|
<CCol className="border-right" lg="2" xl="3" xxl="3">
|
||||||
{deviceConfig?.serialNumber}
|
{deviceConfig?.serialNumber}
|
||||||
|
{' '}
|
||||||
<CopyToClipboardButton t={t} size="sm" content={deviceConfig?.serialNumber} />
|
<CopyToClipboardButton t={t} size="sm" content={deviceConfig?.serialNumber} />
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol lg="2" xl="1" xxl="1">
|
<CCol lg="2" xl="1" xxl="1">
|
||||||
@@ -88,6 +89,7 @@ const DeviceDetails = ({ t, loading, getData, status, deviceConfig, lastStats })
|
|||||||
</CCol>
|
</CCol>
|
||||||
<CCol lg="2" xl="3" xxl="3">
|
<CCol lg="2" xl="3" xxl="3">
|
||||||
{getPassword()}
|
{getPassword()}
|
||||||
|
{' '}
|
||||||
<HideTextButton t={t} toggle={toggleShowPassword} show={showPassword} />
|
<HideTextButton t={t} toggle={toggleShowPassword} show={showPassword} />
|
||||||
<CopyToClipboardButton
|
<CopyToClipboardButton
|
||||||
t={t}
|
t={t}
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ const DeviceListTable = ({
|
|||||||
deleteStatus,
|
deleteStatus,
|
||||||
}) => {
|
}) => {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'deviceType', label: '', filter: false, sorter: false, _style: { width: '3%' } },
|
{ key: 'deviceType', label: '', filter: false, sorter: false, _style: { width: '1%' } },
|
||||||
{ key: 'serialNumber', label: t('common.serial_number'), _style: { width: '6%' } },
|
{ key: 'serialNumber', label: t('common.serial_number'), _style: { width: '6%' } },
|
||||||
{ key: 'firmware', label: t('firmware.revision') },
|
{ key: 'firmware', label: t('firmware.revision') },
|
||||||
{ key: 'firmware_button', label: '', filter: false, _style: { width: '5%' } },
|
{ key: 'firmware_button', label: '', filter: false, _style: { width: '1%' } },
|
||||||
{ key: 'compatible', label: t('common.type'), filter: false, _style: { width: '13%' } },
|
{ key: 'compatible', label: t('common.type'), filter: false, _style: { width: '13%' } },
|
||||||
{ key: 'txBytes', label: 'Tx', filter: false, _style: { width: '14%' } },
|
{ key: 'txBytes', label: 'Tx', filter: false, _style: { width: '14%' } },
|
||||||
{ key: 'rxBytes', label: 'Rx', filter: false, _style: { width: '14%' } },
|
{ key: 'rxBytes', label: 'Rx', filter: false, _style: { width: '14%' } },
|
||||||
@@ -92,14 +92,14 @@ const DeviceListTable = ({
|
|||||||
const tooltipId = createUuid();
|
const tooltipId = createUuid();
|
||||||
let text = t('firmware.unknown_firmware_status');
|
let text = t('firmware.unknown_firmware_status');
|
||||||
let upgradeText = t('firmware.upgrade_to_latest');
|
let upgradeText = t('firmware.upgrade_to_latest');
|
||||||
let icon = <CIcon size="md" name="cil-arrow-circle-top" content={cilArrowCircleTop} />;
|
let icon = <CIcon name="cil-arrow-circle-top" content={cilArrowCircleTop} />;
|
||||||
let color = 'secondary';
|
let color = 'secondary';
|
||||||
if (latest !== undefined) {
|
if (latest !== undefined) {
|
||||||
text = t('firmware.newer_firmware_available');
|
text = t('firmware.newer_firmware_available');
|
||||||
color = 'warning';
|
color = 'warning';
|
||||||
|
|
||||||
if (latest) {
|
if (latest) {
|
||||||
icon = <CIcon size="md" name="cil-check-circle" content={cilCheckCircle} />;
|
icon = <CIcon name="cil-check-circle" content={cilCheckCircle} />;
|
||||||
text = t('firmware.latest_version_installed');
|
text = t('firmware.latest_version_installed');
|
||||||
upgradeText = t('firmware.reinstall_latest');
|
upgradeText = t('firmware.reinstall_latest');
|
||||||
color = 'success';
|
color = 'success';
|
||||||
@@ -147,7 +147,9 @@ const DeviceListTable = ({
|
|||||||
isLoading={upgradeStatus.loading}
|
isLoading={upgradeStatus.loading}
|
||||||
action={() => upgradeToLatest(device)}
|
action={() => upgradeToLatest(device)}
|
||||||
block
|
block
|
||||||
disabled={upgradeStatus.loading}
|
disabled={
|
||||||
|
upgradeStatus.loading && upgradeStatus.serialNumber === device.serialNumber
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
@@ -185,6 +187,7 @@ const DeviceListTable = ({
|
|||||||
const tooltipId = createUuid();
|
const tooltipId = createUuid();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div>
|
||||||
<CPopover content={t('common.delete_device')}>
|
<CPopover content={t('common.delete_device')}>
|
||||||
<div>
|
<div>
|
||||||
<CButton
|
<CButton
|
||||||
@@ -200,6 +203,8 @@ const DeviceListTable = ({
|
|||||||
>
|
>
|
||||||
<CIcon name="cil-trash" content={cilTrash} size="sm" />
|
<CIcon name="cil-trash" content={cilTrash} size="sm" />
|
||||||
</CButton>
|
</CButton>
|
||||||
|
</div>
|
||||||
|
</CPopover>
|
||||||
<ReactTooltip
|
<ReactTooltip
|
||||||
id={tooltipId}
|
id={tooltipId}
|
||||||
place="top"
|
place="top"
|
||||||
@@ -220,6 +225,7 @@ const DeviceListTable = ({
|
|||||||
<CCardHeader color="primary" className={styles.tooltipHeader}>
|
<CCardHeader color="primary" className={styles.tooltipHeader}>
|
||||||
{t('common.device_delete', { serialNumber })}
|
{t('common.device_delete', { serialNumber })}
|
||||||
<CButtonClose
|
<CButtonClose
|
||||||
|
className="p-0 mb-1"
|
||||||
style={{ color: 'white' }}
|
style={{ color: 'white' }}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.target.parentNode.parentNode.classList.remove('show');
|
e.target.parentNode.parentNode.classList.remove('show');
|
||||||
@@ -237,7 +243,11 @@ const DeviceListTable = ({
|
|||||||
label={t('common.confirm')}
|
label={t('common.confirm')}
|
||||||
isLoadingLabel={t('user.deleting')}
|
isLoadingLabel={t('user.deleting')}
|
||||||
isLoading={deleteStatus.loading}
|
isLoading={deleteStatus.loading}
|
||||||
action={() => deleteDevice(serialNumber)}
|
action={(e) => {
|
||||||
|
e.target.parentNode.parentNode.parentNode.parentNode.classList.remove('show');
|
||||||
|
hideTooltips();
|
||||||
|
deleteDevice(serialNumber);
|
||||||
|
}}
|
||||||
block
|
block
|
||||||
disabled={deleteStatus.loading}
|
disabled={deleteStatus.loading}
|
||||||
/>
|
/>
|
||||||
@@ -246,14 +256,13 @@ const DeviceListTable = ({
|
|||||||
</CCardBody>
|
</CCardBody>
|
||||||
</ReactTooltip>
|
</ReactTooltip>
|
||||||
</div>
|
</div>
|
||||||
</CPopover>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CCard className="m-0 p-0">
|
<CCard className="m-0 p-0">
|
||||||
<CCardHeader className="dark-header">
|
<CCardHeader className="p-0">
|
||||||
<div className="float-left" style={{ width: '400px' }}>
|
<div className="float-left" style={{ width: '400px' }}>
|
||||||
{searchBar}
|
{searchBar}
|
||||||
</div>
|
</div>
|
||||||
@@ -335,7 +344,7 @@ const DeviceListTable = ({
|
|||||||
<CButtonToolbar
|
<CButtonToolbar
|
||||||
role="group"
|
role="group"
|
||||||
className="justify-content-center"
|
className="justify-content-center"
|
||||||
style={{ width: '170px' }}
|
style={{ width: '180px' }}
|
||||||
>
|
>
|
||||||
<CPopover content={t('actions.connect')}>
|
<CPopover content={t('actions.connect')}>
|
||||||
<CButton
|
<CButton
|
||||||
|
|||||||
@@ -19,10 +19,12 @@
|
|||||||
font-size: 0.875rem !important;
|
font-size: 0.875rem !important;
|
||||||
font-weight: 400 !important;
|
font-weight: 400 !important;
|
||||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2) !important;
|
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2) !important;
|
||||||
width: 300px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltipHeader {
|
.tooltipHeader {
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 10px;
|
||||||
border-top-left-radius: 1rem !important;
|
border-top-left-radius: 1rem !important;
|
||||||
border-top-right-radius: 1rem !important;
|
border-top-right-radius: 1rem !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Select, { components } from 'react-select';
|
import Select, { components } from 'react-select';
|
||||||
|
|
||||||
const DeviceSearchBar = ({ t, search, results, history }) => {
|
const DeviceSearchBar = ({ t, search, results, history, action }) => {
|
||||||
const [selected, setSelected] = useState('');
|
const [selected, setSelected] = useState('');
|
||||||
const NoOptionsMessage = (props) => (
|
const NoOptionsMessage = (props) => (
|
||||||
<components.NoOptionsMessage {...props}>
|
<components.NoOptionsMessage {...props}>
|
||||||
@@ -25,7 +25,9 @@ const DeviceSearchBar = ({ t, search, results, history }) => {
|
|||||||
inputValue={selected}
|
inputValue={selected}
|
||||||
placeholder={t('common.search')}
|
placeholder={t('common.search')}
|
||||||
onInputChange={onInputChange}
|
onInputChange={onInputChange}
|
||||||
onChange={(property) => history.push(`/devices/${property.value}`)}
|
onChange={(property) =>
|
||||||
|
action === null ? history.push(`/devices/${property.value}`) : action(property.value)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -35,6 +37,11 @@ DeviceSearchBar.propTypes = {
|
|||||||
search: PropTypes.func.isRequired,
|
search: PropTypes.func.isRequired,
|
||||||
results: PropTypes.instanceOf(Array).isRequired,
|
results: PropTypes.instanceOf(Array).isRequired,
|
||||||
history: PropTypes.instanceOf(Object).isRequired,
|
history: PropTypes.instanceOf(Object).isRequired,
|
||||||
|
action: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
DeviceSearchBar.defaultProps = {
|
||||||
|
action: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeviceSearchBar;
|
export default DeviceSearchBar;
|
||||||
|
|||||||
@@ -119,11 +119,18 @@ const DeviceStatusCard = ({
|
|||||||
errorField(t)
|
errorField(t)
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{lastStats?.unit?.load[0] ? (lastStats?.unit?.load[0] * 100).toFixed(2) : '-'}%
|
{lastStats?.unit?.load[0] !== undefined
|
||||||
{' / '}
|
? (lastStats?.unit?.load[0] * 100).toFixed(2)
|
||||||
{lastStats?.unit?.load[1] ? (lastStats?.unit?.load[1] * 100).toFixed(2) : '-'}%
|
: '-'}
|
||||||
{' / '}
|
%{' / '}
|
||||||
{lastStats?.unit?.load[2] ? (lastStats?.unit?.load[2] * 100).toFixed(2) : '-'}%
|
{lastStats?.unit?.load[1] !== undefined
|
||||||
|
? (lastStats?.unit?.load[1] * 100).toFixed(2)
|
||||||
|
: '-'}
|
||||||
|
%{' / '}
|
||||||
|
{lastStats?.unit?.load[2] !== undefined
|
||||||
|
? (lastStats?.unit?.load[2] * 100).toFixed(2)
|
||||||
|
: '-'}
|
||||||
|
%
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CCol>
|
</CCol>
|
||||||
|
|||||||
@@ -191,6 +191,14 @@ const EditConfigurationForm = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CCol>
|
</CCol>
|
||||||
|
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
|
||||||
|
<div>{t('common.created')}:</div>
|
||||||
|
</CLabel>
|
||||||
|
<CCol md="7" lg="4" xl="4" xxl="5">
|
||||||
|
<p className="mt-2 mb-0">
|
||||||
|
<FormattedDate date={fields.created.value} />
|
||||||
|
</p>
|
||||||
|
</CCol>
|
||||||
<CLabel col className="mb-2" md="5" lg="2" xl="2" xxl="1" htmlFor="firmwareRCOnly">
|
<CLabel col className="mb-2" md="5" lg="2" xl="2" xxl="1" htmlFor="firmwareRCOnly">
|
||||||
Only Release Candidates
|
Only Release Candidates
|
||||||
</CLabel>
|
</CLabel>
|
||||||
@@ -206,14 +214,6 @@ const EditConfigurationForm = ({
|
|||||||
disabled={!editing || fields.firmwareUpgrade.value === 'no'}
|
disabled={!editing || fields.firmwareUpgrade.value === 'no'}
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
|
|
||||||
<div>{t('common.created')}:</div>
|
|
||||||
</CLabel>
|
|
||||||
<CCol md="7" lg="4" xl="4" xxl="5">
|
|
||||||
<p className="mt-2 mb-0">
|
|
||||||
<FormattedDate date={fields.created.value} />
|
|
||||||
</p>
|
|
||||||
</CCol>
|
|
||||||
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
|
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
|
||||||
<div>{t('common.modified')}:</div>
|
<div>{t('common.modified')}:</div>
|
||||||
</CLabel>
|
</CLabel>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import CreatableSelect from 'react-select/creatable';
|
import CreatableSelect from 'react-select/creatable';
|
||||||
@@ -14,10 +14,12 @@ import {
|
|||||||
CLink,
|
CLink,
|
||||||
CPopover,
|
CPopover,
|
||||||
CButton,
|
CButton,
|
||||||
|
CCollapse,
|
||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import FormattedDate from '../FormattedDate';
|
import FormattedDate from '../FormattedDate';
|
||||||
import RequiredAsterisk from '../RequiredAsterisk';
|
import RequiredAsterisk from '../RequiredAsterisk';
|
||||||
import selectStyles from '../../utils/selectStyles';
|
import selectStyles from '../../utils/selectStyles';
|
||||||
|
import useToggle from '../../hooks/useToggle';
|
||||||
|
|
||||||
const EditContactForm = ({
|
const EditContactForm = ({
|
||||||
t,
|
t,
|
||||||
@@ -30,6 +32,7 @@ const EditContactForm = ({
|
|||||||
hideEntities,
|
hideEntities,
|
||||||
editing,
|
editing,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [showDropdown, toggleDropdown, setShowDropdown] = useToggle(false);
|
||||||
const [filter, setFilter] = useState('');
|
const [filter, setFilter] = useState('');
|
||||||
|
|
||||||
const onPhonesChange = (v) => updateFieldWithKey('phones', { value: v.map((obj) => obj.value) });
|
const onPhonesChange = (v) => updateFieldWithKey('phones', { value: v.map((obj) => obj.value) });
|
||||||
@@ -52,8 +55,13 @@ const EditContactForm = ({
|
|||||||
{ id: 'entity', value: id },
|
{ id: 'entity', value: id },
|
||||||
{ id: 'entityName', value: name },
|
{ id: 'entityName', value: name },
|
||||||
]);
|
]);
|
||||||
|
toggleDropdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editing) setShowDropdown(false);
|
||||||
|
}, [editing]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CForm>
|
<CForm>
|
||||||
<CRow>
|
<CRow>
|
||||||
@@ -344,7 +352,7 @@ const EditContactForm = ({
|
|||||||
)}
|
)}
|
||||||
</CCol>
|
</CCol>
|
||||||
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
||||||
Landlines
|
{t('location.phones')}
|
||||||
</CLabel>
|
</CLabel>
|
||||||
<CCol sm="4">
|
<CCol sm="4">
|
||||||
<CreatableSelect
|
<CreatableSelect
|
||||||
@@ -355,11 +363,11 @@ const EditContactForm = ({
|
|||||||
components={{ NoOptionsMessage }}
|
components={{ NoOptionsMessage }}
|
||||||
options={[]}
|
options={[]}
|
||||||
value={fields.phones.value.map((opt) => ({ value: opt, label: opt }))}
|
value={fields.phones.value.map((opt) => ({ value: opt, label: opt }))}
|
||||||
placeholder={t('common.type_for_options')}
|
placeholder="+1(202)555-0103"
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
<CLabel className="mb-2" sm="2" col htmlFor="phones">
|
||||||
Mobiles
|
{t('location.mobiles')}
|
||||||
</CLabel>
|
</CLabel>
|
||||||
<CCol sm="4">
|
<CCol sm="4">
|
||||||
<CreatableSelect
|
<CreatableSelect
|
||||||
@@ -370,21 +378,22 @@ const EditContactForm = ({
|
|||||||
components={{ NoOptionsMessage }}
|
components={{ NoOptionsMessage }}
|
||||||
options={[]}
|
options={[]}
|
||||||
value={fields.mobiles.value.map((opt) => ({ value: opt, label: opt }))}
|
value={fields.mobiles.value.map((opt) => ({ value: opt, label: opt }))}
|
||||||
placeholder={t('common.type_for_options')}
|
placeholder="+1(202)555-0103"
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CFormGroup row className="pb-1">
|
<CFormGroup row className="pb-1">
|
||||||
<CLabel sm="2" col htmlFor="title">
|
<CLabel sm="2" col htmlFor="title">
|
||||||
{t('entity.selected_entity')}
|
{t('entity.entity')}
|
||||||
</CLabel>
|
</CLabel>
|
||||||
<CCol sm="4" className="pt-2">
|
<CCol sm="4">
|
||||||
<h6>
|
<CButton className="pl-0" color="link" onClick={toggleDropdown} disabled={!editing}>
|
||||||
{fields.entity.value === '' ? t('entity.need_select_entity') : fields.entityName.value}
|
{fields.entity.value === '' ? t('entity.need_select_entity') : fields.entityName.value}
|
||||||
</h6>
|
</CButton>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CFormGroup>
|
</CFormGroup>
|
||||||
{hideEntities ? null : (
|
{hideEntities ? null : (
|
||||||
|
<CCollapse show={showDropdown}>
|
||||||
<div className="overflow-auto border mb-3" style={{ height: '200px' }}>
|
<div className="overflow-auto border mb-3" style={{ height: '200px' }}>
|
||||||
<CInput
|
<CInput
|
||||||
className="w-50 mb-2"
|
className="w-50 mb-2"
|
||||||
@@ -434,6 +443,7 @@ const EditContactForm = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</CCollapse>
|
||||||
)}
|
)}
|
||||||
</CForm>
|
</CForm>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -80,6 +80,14 @@ const EditEntityForm = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CCol>
|
</CCol>
|
||||||
|
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
|
||||||
|
<div>{t('common.created')}:</div>
|
||||||
|
</CLabel>
|
||||||
|
<CCol md="7" lg="4" xl="4" xxl="5">
|
||||||
|
<div className="mt-2 mb-0">
|
||||||
|
<FormattedDate date={fields.created.value} />
|
||||||
|
</div>
|
||||||
|
</CCol>
|
||||||
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="sourceIp">
|
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="sourceIp">
|
||||||
<div>{t('entity.ip_detection')}:</div>
|
<div>{t('entity.ip_detection')}:</div>
|
||||||
</CLabel>
|
</CLabel>
|
||||||
@@ -100,14 +108,6 @@ const EditEntityForm = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CCol>
|
</CCol>
|
||||||
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
|
|
||||||
<div>{t('common.created')}:</div>
|
|
||||||
</CLabel>
|
|
||||||
<CCol md="7" lg="4" xl="4" xxl="5">
|
|
||||||
<div className="mt-2 mb-0">
|
|
||||||
<FormattedDate date={fields.created.value} />
|
|
||||||
</div>
|
|
||||||
</CCol>
|
|
||||||
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
|
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
|
||||||
<div>{t('common.modified')}:</div>
|
<div>{t('common.modified')}:</div>
|
||||||
</CLabel>
|
</CLabel>
|
||||||
|
|||||||
@@ -1,9 +1,24 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { CForm, CInput, CLabel, CCol, CFormGroup, CFormText, CRow } from '@coreui/react';
|
import {
|
||||||
|
CForm,
|
||||||
|
CDataTable,
|
||||||
|
CLink,
|
||||||
|
CInput,
|
||||||
|
CLabel,
|
||||||
|
CCol,
|
||||||
|
CFormGroup,
|
||||||
|
CFormText,
|
||||||
|
CRow,
|
||||||
|
CButton,
|
||||||
|
CCollapse,
|
||||||
|
CPopover,
|
||||||
|
} from '@coreui/react';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import FormattedDate from '../FormattedDate';
|
||||||
import RequiredAsterisk from '../RequiredAsterisk';
|
import RequiredAsterisk from '../RequiredAsterisk';
|
||||||
import selectStyles from '../../utils/selectStyles';
|
import selectStyles from '../../utils/selectStyles';
|
||||||
|
import useToggle from '../../hooks/useToggle';
|
||||||
|
|
||||||
const EditInventoryTagForm = ({
|
const EditInventoryTagForm = ({
|
||||||
t,
|
t,
|
||||||
@@ -11,9 +26,61 @@ const EditInventoryTagForm = ({
|
|||||||
fields,
|
fields,
|
||||||
updateField,
|
updateField,
|
||||||
updateFieldDirectly,
|
updateFieldDirectly,
|
||||||
|
entities,
|
||||||
|
venues,
|
||||||
deviceTypes,
|
deviceTypes,
|
||||||
editing,
|
editing,
|
||||||
}) => (
|
hideEntities,
|
||||||
|
batchSetField,
|
||||||
|
}) => {
|
||||||
|
const [showDropdown, toggleDropdown, setShowDropdown] = useToggle(false);
|
||||||
|
const [entityFilter, setEntityFilter] = useState('');
|
||||||
|
const [venueFilter, setVenueFilter] = useState('');
|
||||||
|
|
||||||
|
const selectEntity = ({ id, name }) => {
|
||||||
|
batchSetField([
|
||||||
|
{ id: 'entity', value: id },
|
||||||
|
{ id: 'entityName', value: name },
|
||||||
|
{ id: 'venue', value: '' },
|
||||||
|
{ id: 'venueName', value: '' },
|
||||||
|
]);
|
||||||
|
toggleDropdown();
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectVenue = ({ id, name }) => {
|
||||||
|
batchSetField([
|
||||||
|
{ id: 'venue', value: id },
|
||||||
|
{ id: 'venueName', value: name },
|
||||||
|
{ id: 'entity', value: '' },
|
||||||
|
{ id: 'entityName', value: '' },
|
||||||
|
]);
|
||||||
|
toggleDropdown();
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ key: 'created', label: t('common.created'), _style: { width: '20%' }, filter: false },
|
||||||
|
{ key: 'name', label: t('user.name'), _style: { width: '25%' }, filter: false },
|
||||||
|
{ key: 'description', label: t('user.description'), _style: { width: '50%' } },
|
||||||
|
{ key: 'actions', label: '', _style: { width: '15%' }, filter: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEntityLabel = () => {
|
||||||
|
if (fields.entity.value !== '') return t('entity.entity');
|
||||||
|
if (fields.venue.value !== '') return t('inventory.venue');
|
||||||
|
return `${t('entity.entity')}/${t('inventory.venue')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEntityValue = () => {
|
||||||
|
if (fields.entity.value !== '') return fields.entityName.value;
|
||||||
|
if (fields.venue.value !== '') return fields.venueName.value;
|
||||||
|
return t('entity.need_select_entity');
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editing) setShowDropdown(false);
|
||||||
|
}, [editing]);
|
||||||
|
|
||||||
|
return (
|
||||||
<CForm>
|
<CForm>
|
||||||
<CFormGroup row className="mb-1">
|
<CFormGroup row className="mb-1">
|
||||||
<CCol>
|
<CCol>
|
||||||
@@ -119,8 +186,121 @@ const EditInventoryTagForm = ({
|
|||||||
</CFormText>
|
</CFormText>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
|
<CFormGroup row className="pb-1">
|
||||||
|
<CLabel sm="4" col htmlFor="title">
|
||||||
|
{getEntityLabel()}
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="8">
|
||||||
|
<CButton className="pl-0" color="link" onClick={toggleDropdown} disabled={!editing}>
|
||||||
|
{getEntityValue()}
|
||||||
|
</CButton>
|
||||||
|
</CCol>
|
||||||
|
</CFormGroup>
|
||||||
|
{hideEntities ? null : (
|
||||||
|
<CCollapse show={showDropdown}>
|
||||||
|
<div className="overflow-auto border mb-3" style={{ height: '200px' }}>
|
||||||
|
<h5>{t('entity.entities')}</h5>
|
||||||
|
<CInput
|
||||||
|
className="w-50 mb-2"
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
value={entityFilter}
|
||||||
|
onChange={(e) => setEntityFilter(e.target.value)}
|
||||||
|
/>
|
||||||
|
<CDataTable
|
||||||
|
items={entities}
|
||||||
|
fields={columns}
|
||||||
|
hover
|
||||||
|
tableFilterValue={entityFilter}
|
||||||
|
border
|
||||||
|
scopedSlots={{
|
||||||
|
name: (item) => (
|
||||||
|
<td className="align-middle p-1">
|
||||||
|
<CLink
|
||||||
|
className="c-subheader-nav-link"
|
||||||
|
aria-current="page"
|
||||||
|
to={() => `/entity/${item.id}`}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</CLink>
|
||||||
|
</td>
|
||||||
|
),
|
||||||
|
created: (item) => (
|
||||||
|
<td className="align-middle p-1">
|
||||||
|
<FormattedDate date={item.created} />
|
||||||
|
</td>
|
||||||
|
),
|
||||||
|
actions: (item) => (
|
||||||
|
<td className="align-middle p-1">
|
||||||
|
<CPopover content={t('entity.select_entity')}>
|
||||||
|
<CButton
|
||||||
|
size="sm"
|
||||||
|
color="primary"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => selectEntity(item)}
|
||||||
|
disabled={!editing}
|
||||||
|
>
|
||||||
|
{t('common.select')}
|
||||||
|
</CButton>
|
||||||
|
</CPopover>
|
||||||
|
</td>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<h5>{t('entity.venues')}</h5>
|
||||||
|
<CInput
|
||||||
|
className="w-50 mb-2"
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
value={venueFilter}
|
||||||
|
onChange={(e) => setVenueFilter(e.target.value)}
|
||||||
|
/>
|
||||||
|
<CDataTable
|
||||||
|
items={venues}
|
||||||
|
fields={columns}
|
||||||
|
hover
|
||||||
|
tableFilterValue={venueFilter}
|
||||||
|
border
|
||||||
|
scopedSlots={{
|
||||||
|
name: (item) => (
|
||||||
|
<td className="align-middle p-1">
|
||||||
|
<CLink
|
||||||
|
className="c-subheader-nav-link"
|
||||||
|
aria-current="page"
|
||||||
|
to={() => `/entity/${item.id}`}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</CLink>
|
||||||
|
</td>
|
||||||
|
),
|
||||||
|
created: (item) => (
|
||||||
|
<td className="align-middle p-1">
|
||||||
|
<FormattedDate date={item.created} />
|
||||||
|
</td>
|
||||||
|
),
|
||||||
|
actions: (item) => (
|
||||||
|
<td className="align-middle p-1">
|
||||||
|
<CPopover content={t('entity.select_entity')}>
|
||||||
|
<CButton
|
||||||
|
size="sm"
|
||||||
|
color="primary"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => selectVenue(item)}
|
||||||
|
disabled={!editing}
|
||||||
|
>
|
||||||
|
{t('common.select')}
|
||||||
|
</CButton>
|
||||||
|
</CPopover>
|
||||||
|
</td>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CCollapse>
|
||||||
|
)}
|
||||||
</CForm>
|
</CForm>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
EditInventoryTagForm.propTypes = {
|
EditInventoryTagForm.propTypes = {
|
||||||
t: PropTypes.func.isRequired,
|
t: PropTypes.func.isRequired,
|
||||||
@@ -130,6 +310,14 @@ EditInventoryTagForm.propTypes = {
|
|||||||
updateFieldDirectly: PropTypes.func.isRequired,
|
updateFieldDirectly: PropTypes.func.isRequired,
|
||||||
deviceTypes: PropTypes.instanceOf(Array).isRequired,
|
deviceTypes: PropTypes.instanceOf(Array).isRequired,
|
||||||
editing: PropTypes.bool.isRequired,
|
editing: PropTypes.bool.isRequired,
|
||||||
|
entities: PropTypes.instanceOf(Array).isRequired,
|
||||||
|
venues: PropTypes.instanceOf(Array).isRequired,
|
||||||
|
hideEntities: PropTypes.bool,
|
||||||
|
batchSetField: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
EditInventoryTagForm.defaultProps = {
|
||||||
|
hideEntities: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EditInventoryTagForm;
|
export default EditInventoryTagForm;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import CreatableSelect from 'react-select/creatable';
|
import CreatableSelect from 'react-select/creatable';
|
||||||
@@ -14,10 +14,12 @@ import {
|
|||||||
CLink,
|
CLink,
|
||||||
CPopover,
|
CPopover,
|
||||||
CButton,
|
CButton,
|
||||||
|
CCollapse,
|
||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import countryList from 'utils/countryList';
|
import countryList from 'utils/countryList';
|
||||||
import FormattedDate from '../FormattedDate';
|
import FormattedDate from '../FormattedDate';
|
||||||
import RequiredAsterisk from '../RequiredAsterisk';
|
import RequiredAsterisk from '../RequiredAsterisk';
|
||||||
|
import useToggle from '../../hooks/useToggle';
|
||||||
|
|
||||||
const EditLocationForm = ({
|
const EditLocationForm = ({
|
||||||
t,
|
t,
|
||||||
@@ -30,6 +32,7 @@ const EditLocationForm = ({
|
|||||||
batchSetField,
|
batchSetField,
|
||||||
editing,
|
editing,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [showDropdown, toggleDropdown, setShowDropdown] = useToggle(false);
|
||||||
const [filter, setFilter] = useState('');
|
const [filter, setFilter] = useState('');
|
||||||
|
|
||||||
const onPhonesChange = (v) => updateFieldWithKey('phones', { value: v.map((obj) => obj.value) });
|
const onPhonesChange = (v) => updateFieldWithKey('phones', { value: v.map((obj) => obj.value) });
|
||||||
@@ -52,8 +55,13 @@ const EditLocationForm = ({
|
|||||||
{ id: 'entity', value: id },
|
{ id: 'entity', value: id },
|
||||||
{ id: 'entityName', value: name },
|
{ id: 'entityName', value: name },
|
||||||
]);
|
]);
|
||||||
|
toggleDropdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editing) setShowDropdown(false);
|
||||||
|
}, [editing]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CForm>
|
<CForm>
|
||||||
<CRow>
|
<CRow>
|
||||||
@@ -153,7 +161,7 @@ const EditLocationForm = ({
|
|||||||
)}
|
)}
|
||||||
</CCol>
|
</CCol>
|
||||||
<CLabel className="mb-3" sm="2" col htmlFor="phones">
|
<CLabel className="mb-3" sm="2" col htmlFor="phones">
|
||||||
Landlines
|
{t('location.phones')}
|
||||||
</CLabel>
|
</CLabel>
|
||||||
<CCol sm="4">
|
<CCol sm="4">
|
||||||
<CreatableSelect
|
<CreatableSelect
|
||||||
@@ -164,11 +172,11 @@ const EditLocationForm = ({
|
|||||||
components={{ NoOptionsMessage }}
|
components={{ NoOptionsMessage }}
|
||||||
options={[]}
|
options={[]}
|
||||||
value={fields.phones.value.map((opt) => ({ value: opt, label: opt }))}
|
value={fields.phones.value.map((opt) => ({ value: opt, label: opt }))}
|
||||||
placeholder={t('common.type_for_options')}
|
placeholder="+1(202)555-0103"
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CLabel className="mb-3" sm="2" col htmlFor="phones">
|
<CLabel className="mb-3" sm="2" col htmlFor="mobiles">
|
||||||
Mobiles
|
{t('location.mobiles')}
|
||||||
</CLabel>
|
</CLabel>
|
||||||
<CCol sm="4">
|
<CCol sm="4">
|
||||||
<CreatableSelect
|
<CreatableSelect
|
||||||
@@ -179,7 +187,7 @@ const EditLocationForm = ({
|
|||||||
components={{ NoOptionsMessage }}
|
components={{ NoOptionsMessage }}
|
||||||
options={[]}
|
options={[]}
|
||||||
value={fields.mobiles.value.map((opt) => ({ value: opt, label: opt }))}
|
value={fields.mobiles.value.map((opt) => ({ value: opt, label: opt }))}
|
||||||
placeholder={t('common.type_for_options')}
|
placeholder="+1(202)555-0103"
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol className="mb-3" sm="12">
|
<CCol className="mb-3" sm="12">
|
||||||
@@ -339,14 +347,15 @@ const EditLocationForm = ({
|
|||||||
</CRow>
|
</CRow>
|
||||||
<CFormGroup row className="pt-2 pb-1">
|
<CFormGroup row className="pt-2 pb-1">
|
||||||
<CLabel sm="2" col htmlFor="title">
|
<CLabel sm="2" col htmlFor="title">
|
||||||
{t('entity.selected_entity')}
|
{t('entity.entity')}
|
||||||
</CLabel>
|
</CLabel>
|
||||||
<CCol sm="4" className="pt-2">
|
<CCol sm="4">
|
||||||
<h6>
|
<CButton className="pl-0" color="link" onClick={toggleDropdown} disabled={!editing}>
|
||||||
{fields.entity.value === '' ? t('entity.need_select_entity') : fields.entityName.value}
|
{fields.entity.value === '' ? t('entity.need_select_entity') : fields.entityName.value}
|
||||||
</h6>
|
</CButton>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CFormGroup>
|
</CFormGroup>
|
||||||
|
<CCollapse show={showDropdown}>
|
||||||
<div className="overflow-auto border mb-1" style={{ height: '200px' }}>
|
<div className="overflow-auto border mb-1" style={{ height: '200px' }}>
|
||||||
<CInput
|
<CInput
|
||||||
className="w-50 mb-2"
|
className="w-50 mb-2"
|
||||||
@@ -396,6 +405,7 @@ const EditLocationForm = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</CCollapse>
|
||||||
</CForm>
|
</CForm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
554
src/components/EditSimulationForm/index.js
Normal file
554
src/components/EditSimulationForm/index.js
Normal file
@@ -0,0 +1,554 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {
|
||||||
|
CForm,
|
||||||
|
CInput,
|
||||||
|
CLabel,
|
||||||
|
CCol,
|
||||||
|
CFormText,
|
||||||
|
CRow,
|
||||||
|
CInputFile,
|
||||||
|
CInvalidFeedback,
|
||||||
|
CSelect,
|
||||||
|
} from '@coreui/react';
|
||||||
|
import RequiredAsterisk from '../RequiredAsterisk';
|
||||||
|
|
||||||
|
const validatePem = (value) =>
|
||||||
|
(value.includes('---BEGIN CERTIFICATE---') && value.includes('---END CERTIFICATE---')) ||
|
||||||
|
(value.includes('---BEGIN PRIVATE KEY---') && value.includes('---END PRIVATE KEY---'));
|
||||||
|
|
||||||
|
const EditSimulationForm = ({
|
||||||
|
t,
|
||||||
|
show,
|
||||||
|
disable,
|
||||||
|
fields,
|
||||||
|
updateField,
|
||||||
|
updateFieldWithKey,
|
||||||
|
editing,
|
||||||
|
}) => {
|
||||||
|
const [certKey, setCertKey] = useState(0);
|
||||||
|
const [keyKey, setKeyKey] = useState(0);
|
||||||
|
|
||||||
|
let fileReader;
|
||||||
|
|
||||||
|
const handleCertFileRead = () => {
|
||||||
|
const content = fileReader.result;
|
||||||
|
if (content && validatePem(content)) {
|
||||||
|
updateFieldWithKey('certificate', { value: content, error: false });
|
||||||
|
} else {
|
||||||
|
updateFieldWithKey('certificate', { error: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCertFile = (file) => {
|
||||||
|
fileReader = new FileReader();
|
||||||
|
fileReader.onloadend = handleCertFileRead;
|
||||||
|
fileReader.readAsText(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyFileRead = () => {
|
||||||
|
updateFieldWithKey('key', { error: false });
|
||||||
|
const content = fileReader.result;
|
||||||
|
if (content && validatePem(content)) {
|
||||||
|
updateFieldWithKey('key', { value: content, error: false });
|
||||||
|
} else {
|
||||||
|
updateFieldWithKey('key', { error: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyFile = (file) => {
|
||||||
|
fileReader = new FileReader();
|
||||||
|
fileReader.onloadend = handleKeyFileRead;
|
||||||
|
fileReader.readAsText(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (show) {
|
||||||
|
setCertKey(certKey + 1);
|
||||||
|
setKeyKey(keyKey + 1);
|
||||||
|
}
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CForm>
|
||||||
|
<CRow>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="name">
|
||||||
|
{t('user.name')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="name"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={fields.name.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={fields.name.error}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
maxLength="50"
|
||||||
|
/>
|
||||||
|
<CFormText hidden={!fields.name.error} color={fields.name.error ? 'danger' : ''}>
|
||||||
|
{t('common.required')}
|
||||||
|
</CFormText>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="gateway">
|
||||||
|
{t('simulation.gateway')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="gateway"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={fields.gateway.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={fields.gateway.error}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
maxLength="50"
|
||||||
|
/>
|
||||||
|
<CFormText hidden={!fields.gateway.error} color={fields.gateway.error ? 'danger' : ''}>
|
||||||
|
{t('common.required')}
|
||||||
|
</CFormText>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="certificate">
|
||||||
|
{t('common.certificate')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
{editing ? (
|
||||||
|
<div>
|
||||||
|
<CInputFile
|
||||||
|
className="mt-1"
|
||||||
|
key={certKey}
|
||||||
|
id="file-input"
|
||||||
|
name="file-input"
|
||||||
|
accept=".pem"
|
||||||
|
onChange={(e) => handleCertFile(e.target.files[0])}
|
||||||
|
/>
|
||||||
|
<CFormText
|
||||||
|
hidden={!fields.certificate.error}
|
||||||
|
color={fields.certificate.error ? 'danger' : ''}
|
||||||
|
>
|
||||||
|
{t('common.required')}
|
||||||
|
</CFormText>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="pt-1 mt-1">{t('simulation.valid_cert')}</div>
|
||||||
|
)}
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="key">
|
||||||
|
{t('common.key')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
{editing ? (
|
||||||
|
<div>
|
||||||
|
<CInputFile
|
||||||
|
className="mt-1"
|
||||||
|
key={keyKey}
|
||||||
|
id="file-input"
|
||||||
|
name="file-input"
|
||||||
|
accept=".pem"
|
||||||
|
onChange={(e) => handleKeyFile(e.target.files[0])}
|
||||||
|
/>
|
||||||
|
<CFormText hidden={!fields.key.error} color={fields.key.error ? 'danger' : ''}>
|
||||||
|
{t('common.required')}
|
||||||
|
</CFormText>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="pt-1 mt-1">{t('simulation.valid_key')}</div>
|
||||||
|
)}
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="macPrefix">
|
||||||
|
{t('simulation.mac_prefix')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="macPrefix"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={fields.macPrefix.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={fields.macPrefix.error}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
maxLength="50"
|
||||||
|
/>
|
||||||
|
<CFormText
|
||||||
|
hidden={!fields.macPrefix.error}
|
||||||
|
color={fields.macPrefix.error ? 'danger' : ''}
|
||||||
|
>
|
||||||
|
{t('simulation.prefix_length')}
|
||||||
|
</CFormText>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="devices">
|
||||||
|
{t('common.devices')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="devices"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.devices.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.devices.value < fields.devices.min || fields.devices.value > fields.devices.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CFormText hidden={!fields.devices.error} color={fields.devices.error ? 'danger' : ''}>
|
||||||
|
{t('common.required')}
|
||||||
|
</CFormText>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="healthCheckInterval">
|
||||||
|
{t('simulation.healtcheck_interval')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="healthCheckInterval"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.healthCheckInterval.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.healthCheckInterval.value < fields.healthCheckInterval.min ||
|
||||||
|
fields.healthCheckInterval.value > fields.healthCheckInterval.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.healthCheckInterval.min,
|
||||||
|
max: fields.healthCheckInterval.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="stateInterval">
|
||||||
|
{t('simulation.state_interval')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="stateInterval"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.stateInterval.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.stateInterval.value < fields.stateInterval.min ||
|
||||||
|
fields.stateInterval.value > fields.stateInterval.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.stateInterval.min,
|
||||||
|
max: fields.stateInterval.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="clientInterval">
|
||||||
|
{t('simulation.client_interval')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="clientInterval"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.clientInterval.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.clientInterval.value < fields.clientInterval.min ||
|
||||||
|
fields.clientInterval.value > fields.clientInterval.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.clientInterval.min,
|
||||||
|
max: fields.clientInterval.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="reconnectInterval">
|
||||||
|
{t('simulation.reconnect_interval')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="reconnectInterval"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.reconnectInterval.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.reconnectInterval.value < fields.reconnectInterval.min ||
|
||||||
|
fields.reconnectInterval.value > fields.reconnectInterval.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.reconnectInterval.min,
|
||||||
|
max: fields.reconnectInterval.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="minAssociations">
|
||||||
|
{t('simulation.min_associations')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="minAssociations"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.minAssociations.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.minAssociations.value < fields.minAssociations.min ||
|
||||||
|
fields.minAssociations.value > fields.minAssociations.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.minAssociations.min,
|
||||||
|
max: fields.minAssociations.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="maxAssociations">
|
||||||
|
{t('simulation.max_associations')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="maxAssociations"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.maxAssociations.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.maxAssociations.value < fields.maxAssociations.min ||
|
||||||
|
fields.maxAssociations.value > fields.maxAssociations.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.maxAssociations.min,
|
||||||
|
max: fields.maxAssociations.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="minClients">
|
||||||
|
{t('simulation.min_clients')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="minClients"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.minClients.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.minClients.value < fields.minClients.min ||
|
||||||
|
fields.minClients.value > fields.minClients.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.minClients.min,
|
||||||
|
max: fields.minClients.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="maxClients">
|
||||||
|
{t('simulation.max_clients')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="maxClients"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.maxClients.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.maxClients.value < fields.maxClients.min ||
|
||||||
|
fields.maxClients.value > fields.maxClients.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.maxClients.min,
|
||||||
|
max: fields.maxClients.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="simulationLength">
|
||||||
|
{t('simulation.length')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="simulationLength"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.simulationLength.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.simulationLength.value < fields.simulationLength.min ||
|
||||||
|
fields.simulationLength.value > fields.simulationLength.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.simulationLength.min,
|
||||||
|
max: fields.simulationLength.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="threads">
|
||||||
|
{t('simulation.threads')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="threads"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.threads.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.threads.value < fields.threads.min || fields.threads.value > fields.threads.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.threads.min,
|
||||||
|
max: fields.threads.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="keepAlive">
|
||||||
|
{t('simulation.keep_alive')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CInput
|
||||||
|
id="keepAlive"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
value={fields.keepAlive.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={
|
||||||
|
fields.keepAlive.value < fields.keepAlive.min ||
|
||||||
|
fields.keepAlive.value > fields.keepAlive.max
|
||||||
|
}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
pattern="[0-9]*"
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>
|
||||||
|
{t('common.min_max', {
|
||||||
|
min: fields.keepAlive.min,
|
||||||
|
max: fields.keepAlive.max,
|
||||||
|
})}
|
||||||
|
</CInvalidFeedback>
|
||||||
|
</CCol>
|
||||||
|
<CLabel className="mb-2" sm="2" col htmlFor="deviceType">
|
||||||
|
{t('configuration.device_type')}
|
||||||
|
<RequiredAsterisk />
|
||||||
|
</CLabel>
|
||||||
|
<CCol sm="4">
|
||||||
|
<CSelect
|
||||||
|
custom
|
||||||
|
id="deviceType"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={fields.deviceType.value}
|
||||||
|
onChange={updateField}
|
||||||
|
invalid={fields.deviceType.error}
|
||||||
|
disabled={disable || !editing}
|
||||||
|
maxLength="50"
|
||||||
|
>
|
||||||
|
<option value="cig_wf160d">cig_wf160d</option>
|
||||||
|
<option value="cig_wf188">cig_wf188</option>
|
||||||
|
<option value="cig_wf188n">cig_wf188n</option>
|
||||||
|
<option value="cig_wf194c">cig_wf194c</option>
|
||||||
|
<option value="cig_wf194c4">cig_wf194c4</option>
|
||||||
|
<option value="edgecore_eap101">edgecore_eap101</option>
|
||||||
|
<option value="edgecore_eap102">edgecore_eap102</option>
|
||||||
|
<option value="edgecore_ecs4100-12ph">edgecore_ecs4100-12ph</option>
|
||||||
|
<option value="edgecore_ecw5211">edgecore_ecw5211</option>
|
||||||
|
<option value="edgecore_ecw5410">edgecore_ecw5410</option>
|
||||||
|
<option value="edgecore_oap100">edgecore_oap100</option>
|
||||||
|
<option value="edgecore_spw2ac1200">edgecore_spw2ac1200</option>
|
||||||
|
<option value="edgecore_spw2ac1200-lan-poe">edgecore_spw2ac1200-lan-poe</option>
|
||||||
|
<option value="edgecore_ssw2ac2600">edgecore_ssw2ac2600</option>
|
||||||
|
<option value="hfcl_ion4.yml">hfcl_ion4.yml</option>
|
||||||
|
<option value="indio_um-305ac">indio_um-305ac</option>
|
||||||
|
<option value="linksys_e8450-ubi">linksys_e8450-ubi</option>
|
||||||
|
<option value="linksys_ea6350">linksys_ea6350</option>
|
||||||
|
<option value="linksys_ea6350-v4">linksys_ea6350-v4</option>
|
||||||
|
<option value="linksys_ea8300">linksys_ea8300</option>
|
||||||
|
<option value="mikrotik_nand">mikrotik_nand</option>
|
||||||
|
<option value="tp-link_ec420-g1">tp-link_ec420-g1</option>
|
||||||
|
<option value="tplink_cpe210_v3">tplink_cpe210_v3</option>
|
||||||
|
<option value="tplink_cpe510_v3">tplink_cpe510_v3</option>
|
||||||
|
<option value="tplink_eap225_outdoor_v1">tplink_eap225_outdoor_v1</option>
|
||||||
|
<option value="tplink_ec420">tplink_ec420</option>
|
||||||
|
<option value="tplink_ex227">tplink_ex227</option>
|
||||||
|
<option value="tplink_ex228">tplink_ex228</option>
|
||||||
|
<option value="tplink_ex447">tplink_ex447</option>
|
||||||
|
<option value="wallys_dr40x9">wallys_dr40x9</option>
|
||||||
|
</CSelect>
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
</CForm>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
EditSimulationForm.propTypes = {
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
|
show: PropTypes.bool.isRequired,
|
||||||
|
disable: PropTypes.bool.isRequired,
|
||||||
|
fields: PropTypes.instanceOf(Object).isRequired,
|
||||||
|
updateField: PropTypes.func.isRequired,
|
||||||
|
updateFieldWithKey: PropTypes.func.isRequired,
|
||||||
|
editing: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditSimulationForm;
|
||||||
81
src/components/EntityTree/index.js
Normal file
81
src/components/EntityTree/index.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ReactFlow, { removeElements, MiniMap, Controls, Background } from 'react-flow-renderer';
|
||||||
|
|
||||||
|
const EntityTree = ({ elements, setElements, history, toggle, setReactFlowInstance }) => {
|
||||||
|
const onElementsRemove = (elementsToRemove) => {
|
||||||
|
setElements((els) => removeElements(elementsToRemove, els));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClick = (e, el) => {
|
||||||
|
const split = el.id.split('/');
|
||||||
|
const type = split[0];
|
||||||
|
|
||||||
|
if (type === 'entity' || type === 'world') {
|
||||||
|
toggle();
|
||||||
|
history.push(`/entity/${split[1]}`);
|
||||||
|
} else if (type === 'venue') {
|
||||||
|
toggle();
|
||||||
|
history.push(`/venue/${split[1]}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLoad = (instance) => {
|
||||||
|
setReactFlowInstance(instance);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: '80vh', width: '100%' }}>
|
||||||
|
<ReactFlow
|
||||||
|
elements={elements}
|
||||||
|
onElementsRemove={onElementsRemove}
|
||||||
|
onElementClick={onClick}
|
||||||
|
deleteKeyCode={null}
|
||||||
|
onLoad={onLoad}
|
||||||
|
snapToGrid
|
||||||
|
snapGrid={[10, 10]}
|
||||||
|
>
|
||||||
|
<div className="float-left">
|
||||||
|
<div
|
||||||
|
className="align-middle text-center mx-auto"
|
||||||
|
style={{ backgroundColor: '#0F0A0A', color: 'white', width: '150px' }}
|
||||||
|
>
|
||||||
|
<h4 className="align-middle mb-0 font-weight-bold">Root</h4>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="align-middle text-center mx-auto"
|
||||||
|
style={{ backgroundColor: '#2292A4', color: 'white', width: '150px' }}
|
||||||
|
>
|
||||||
|
<h4 className="align-middle mb-0 font-weight-bold">Entity</h4>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="align-middle text-center mx-auto"
|
||||||
|
style={{ backgroundColor: '#F5EFED', width: '150px' }}
|
||||||
|
>
|
||||||
|
<h4 className="align-middle mb-0 font-weight-bold">Venue</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<MiniMap
|
||||||
|
nodeColor={(n) => {
|
||||||
|
if (n.style?.background) return n.style.background;
|
||||||
|
|
||||||
|
return '#fff';
|
||||||
|
}}
|
||||||
|
nodeBorderRadius={5}
|
||||||
|
/>
|
||||||
|
<Controls />
|
||||||
|
<Background color="#aaa" gap={20} />
|
||||||
|
</ReactFlow>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
EntityTree.propTypes = {
|
||||||
|
elements: PropTypes.instanceOf(Array).isRequired,
|
||||||
|
setElements: PropTypes.func.isRequired,
|
||||||
|
history: PropTypes.instanceOf(Object).isRequired,
|
||||||
|
toggle: PropTypes.func.isRequired,
|
||||||
|
setReactFlowInstance: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(EntityTree);
|
||||||
@@ -18,7 +18,15 @@ const validatePem = (value) =>
|
|||||||
(value.includes('---BEGIN CERTIFICATE---') && value.includes('---END CERTIFICATE---')) ||
|
(value.includes('---BEGIN CERTIFICATE---') && value.includes('---END CERTIFICATE---')) ||
|
||||||
(value.includes('---BEGIN PRIVATE KEY---') && value.includes('---END PRIVATE KEY---'));
|
(value.includes('---BEGIN PRIVATE KEY---') && value.includes('---END PRIVATE KEY---'));
|
||||||
|
|
||||||
const FileToStringButton = ({ t, save, title, explanations, acceptedFileTypes, size }) => {
|
const FileToStringButton = ({
|
||||||
|
t,
|
||||||
|
save,
|
||||||
|
title,
|
||||||
|
explanations,
|
||||||
|
acceptedFileTypes,
|
||||||
|
size,
|
||||||
|
disabled,
|
||||||
|
}) => {
|
||||||
const [show, toggle] = useToggle(false);
|
const [show, toggle] = useToggle(false);
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
const [fileName, setFileName] = useState('');
|
const [fileName, setFileName] = useState('');
|
||||||
@@ -74,6 +82,7 @@ const FileToStringButton = ({ t, save, title, explanations, acceptedFileTypes, s
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
style={{ height: '35px', width: '35px' }}
|
style={{ height: '35px', width: '35px' }}
|
||||||
size={size}
|
size={size}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<CIcon content={cilCloudUpload} />
|
<CIcon content={cilCloudUpload} />
|
||||||
</CButton>
|
</CButton>
|
||||||
@@ -125,6 +134,7 @@ FileToStringButton.propTypes = {
|
|||||||
explanations: PropTypes.string.isRequired,
|
explanations: PropTypes.string.isRequired,
|
||||||
acceptedFileTypes: PropTypes.string.isRequired,
|
acceptedFileTypes: PropTypes.string.isRequired,
|
||||||
size: PropTypes.string,
|
size: PropTypes.string,
|
||||||
|
disabled: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
FileToStringButton.defaultProps = {
|
FileToStringButton.defaultProps = {
|
||||||
|
|||||||
@@ -2,37 +2,32 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CCardBody, CCol, CInput, CRow } from '@coreui/react';
|
import { CCardBody, CCol, CInput, CRow } from '@coreui/react';
|
||||||
import { prettyDate, cleanBytesString } from '../../utils/formatting';
|
import { prettyDate, cleanBytesString } from '../../utils/formatting';
|
||||||
import NotesTable from '../NotesTable';
|
|
||||||
|
|
||||||
const FirmwareDetailsForm = ({ t, fields, updateFieldsWithId, addNote, editing }) => (
|
const FirmwareDetailsForm = ({ t, fields, updateFieldsWithId, editing }) => (
|
||||||
<CCardBody className="p-1">
|
<CCardBody className="p-1">
|
||||||
<CRow>
|
<CRow>
|
||||||
<CCol sm="2">Created</CCol>
|
<CCol sm="2">{t('firmware.release')}</CCol>
|
||||||
<CCol sm="4">{prettyDate(fields.created.value)}</CCol>
|
|
||||||
<CCol sm="2">Release</CCol>
|
|
||||||
<CCol sm="4">{fields.release.value}</CCol>
|
<CCol sm="4">{fields.release.value}</CCol>
|
||||||
|
<CCol sm="2">{t('common.created')}</CCol>
|
||||||
|
<CCol sm="4">{prettyDate(fields.created.value)}</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className="my-3">
|
<CRow className="my-3">
|
||||||
<CCol sm="2">Image</CCol>
|
<CCol sm="2">{t('firmware.image_date')}</CCol>
|
||||||
<CCol sm="4">{fields.image.value}</CCol>
|
|
||||||
<CCol sm="2">Image Date</CCol>
|
|
||||||
<CCol sm="4">{prettyDate(fields.imageDate.value)}</CCol>
|
<CCol sm="4">{prettyDate(fields.imageDate.value)}</CCol>
|
||||||
|
<CCol sm="2">{t('firmware.size')}</CCol>
|
||||||
|
<CCol sm="4">{cleanBytesString(fields.size.value)}</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className="my-3">
|
<CRow className="my-3">
|
||||||
<CCol sm="2">Revision</CCol>
|
<CCol sm="2">{t('firmware.image')}</CCol>
|
||||||
|
<CCol sm="4">{fields.image.value}</CCol>
|
||||||
|
<CCol sm="2">{t('firmware.revision')}</CCol>
|
||||||
<CCol sm="4">{fields.revision.value}</CCol>
|
<CCol sm="4">{fields.revision.value}</CCol>
|
||||||
<CCol sm="2">Size</CCol>
|
|
||||||
<CCol sm="4">{cleanBytesString(fields.size.value)}</CCol>
|
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className="my-3">
|
<CRow className="my-3">
|
||||||
<CCol sm="2">URI</CCol>
|
<CCol sm="2">URI</CCol>
|
||||||
<CCol sm="4">{fields.uri.value}</CCol>
|
<CCol sm="4">{fields.uri.value}</CCol>
|
||||||
<CCol sm="2">Owner</CCol>
|
|
||||||
<CCol sm="4">{fields.owner.value === '' ? t('common.unknown') : fields.owner.value}</CCol>
|
|
||||||
</CRow>
|
|
||||||
<CRow className="my-3">
|
|
||||||
<CCol sm="2" className="mt-2">
|
<CCol sm="2" className="mt-2">
|
||||||
Description
|
{t('user.description')}
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol sm="4">
|
<CCol sm="4">
|
||||||
{editing ? (
|
{editing ? (
|
||||||
@@ -46,9 +41,6 @@ const FirmwareDetailsForm = ({ t, fields, updateFieldsWithId, addNote, editing }
|
|||||||
<p className="mt-2 mb-0">{fields.description.value}</p>
|
<p className="mt-2 mb-0">{fields.description.value}</p>
|
||||||
)}
|
)}
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
|
||||||
<NotesTable t={t} notes={fields.notes.value} addNote={addNote} editable={editing} />
|
|
||||||
</CCol>
|
|
||||||
</CRow>
|
</CRow>
|
||||||
</CCardBody>
|
</CCardBody>
|
||||||
);
|
);
|
||||||
@@ -57,7 +49,6 @@ FirmwareDetailsForm.propTypes = {
|
|||||||
t: PropTypes.func.isRequired,
|
t: PropTypes.func.isRequired,
|
||||||
fields: PropTypes.instanceOf(Object).isRequired,
|
fields: PropTypes.instanceOf(Object).isRequired,
|
||||||
updateFieldsWithId: PropTypes.func.isRequired,
|
updateFieldsWithId: PropTypes.func.isRequired,
|
||||||
addNote: PropTypes.func.isRequired,
|
|
||||||
editing: PropTypes.bool.isRequired,
|
editing: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
export default FirmwareDetailsForm;
|
export default FirmwareDetailsForm;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const FirmwareList = ({
|
|||||||
{ key: 'size', label: t('firmware.size'), _style: { width: '1%' } },
|
{ key: 'size', label: t('firmware.size'), _style: { width: '1%' } },
|
||||||
{ key: 'revision', label: t('firmware.revision'), _style: { width: '1%' } },
|
{ key: 'revision', label: t('firmware.revision'), _style: { width: '1%' } },
|
||||||
{ key: 'uri', label: 'URI' },
|
{ key: 'uri', label: 'URI' },
|
||||||
{ key: 'show_details', label: '', _style: { width: '5%' } },
|
{ key: 'show_details', label: '', _style: { width: '1%' } },
|
||||||
];
|
];
|
||||||
|
|
||||||
const getShortRevision = (revision) => {
|
const getShortRevision = (revision) => {
|
||||||
@@ -54,7 +54,7 @@ const FirmwareList = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<CCard className="m-0">
|
<CCard className="m-0">
|
||||||
<CCardHeader className="dark-header">
|
<CCardHeader className="p-1">
|
||||||
<div className="d-flex flex-row-reverse">
|
<div className="d-flex flex-row-reverse">
|
||||||
<div className="px-3">
|
<div className="px-3">
|
||||||
<CSwitch
|
<CSwitch
|
||||||
@@ -113,7 +113,7 @@ const FirmwareList = ({
|
|||||||
),
|
),
|
||||||
uri: (item) => (
|
uri: (item) => (
|
||||||
<td className="align-middle">
|
<td className="align-middle">
|
||||||
<div style={{ width: 'calc(45vw)' }}>
|
<div style={{ width: 'calc(50vw)' }}>
|
||||||
<div className="text-truncate align-middle">
|
<div className="text-truncate align-middle">
|
||||||
<CopyToClipboardButton key={item.uri} t={t} size="sm" content={item.uri} />
|
<CopyToClipboardButton key={item.uri} t={t} size="sm" content={item.uri} />
|
||||||
<CPopover content={item.uri}>
|
<CPopover content={item.uri}>
|
||||||
@@ -125,6 +125,7 @@ const FirmwareList = ({
|
|||||||
),
|
),
|
||||||
show_details: (item) => (
|
show_details: (item) => (
|
||||||
<td className="text-center align-middle">
|
<td className="text-center align-middle">
|
||||||
|
<CPopover content={t('common.details')}>
|
||||||
<CButton
|
<CButton
|
||||||
size="sm"
|
size="sm"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -133,6 +134,7 @@ const FirmwareList = ({
|
|||||||
>
|
>
|
||||||
<CIcon content={cilSearch} />
|
<CIcon content={cilSearch} />
|
||||||
</CButton>
|
</CButton>
|
||||||
|
</CPopover>
|
||||||
</td>
|
</td>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { CPopover } from '@coreui/react';
|
|||||||
import { formatDaysAgo, prettyDate } from 'utils/formatting';
|
import { formatDaysAgo, prettyDate } from 'utils/formatting';
|
||||||
|
|
||||||
const FormattedDate = ({ date }) => (
|
const FormattedDate = ({ date }) => (
|
||||||
<CPopover content={prettyDate(date)}>
|
<CPopover content={prettyDate(date)} advancedOptions={{ animation: false }}>
|
||||||
<span className="d-inline-block">{date === 0 ? '-' : formatDaysAgo(date)}</span>
|
<span className="d-inline-block">{date === 0 ? '-' : formatDaysAgo(date)}</span>
|
||||||
</CPopover>
|
</CPopover>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { CButton, CPopover } from '@coreui/react';
|
|||||||
|
|
||||||
const HideTextButton = ({ t, toggle, show, size }) => (
|
const HideTextButton = ({ t, toggle, show, size }) => (
|
||||||
<CPopover content={t('user.show_hide_password')}>
|
<CPopover content={t('user.show_hide_password')}>
|
||||||
<CButton onClick={toggle} size={size} className="pt-0">
|
<CButton onClick={toggle} size={size} className="py-0">
|
||||||
<CIcon name={show ? 'cil-envelope-open' : 'cil-envelope-closed'} />
|
<CIcon name={show ? 'cil-envelope-open' : 'cil-envelope-closed'} />
|
||||||
</CButton>
|
</CButton>
|
||||||
</CPopover>
|
</CPopover>
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ const InventoryTable = ({
|
|||||||
<CIcon name="cil-spreadsheet" content={cilSpreadsheet} size="sm" />
|
<CIcon name="cil-spreadsheet" content={cilSpreadsheet} size="sm" />
|
||||||
</CButton>
|
</CButton>
|
||||||
</CPopover>
|
</CPopover>
|
||||||
<CPopover content="View Tag">
|
<CPopover content={t('common.details')}>
|
||||||
<CButton
|
<CButton
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CButton, CDataTable, CLink, CPopover, CButtonToolbar } from '@coreui/react';
|
import { CButton, CDataTable, CLink, CPopover, CButtonToolbar } from '@coreui/react';
|
||||||
import { cilPencil, cilPlus } from '@coreui/icons';
|
import { cilMagnifyingGlass, cilPlus } from '@coreui/icons';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
import ReactTooltip from 'react-tooltip';
|
import ReactTooltip from 'react-tooltip';
|
||||||
import DeleteButton from './DeleteButton';
|
import DeleteButton from './DeleteButton';
|
||||||
@@ -118,7 +118,7 @@ const LocationTable = ({
|
|||||||
deleteLocation={deleteLocation}
|
deleteLocation={deleteLocation}
|
||||||
hideTooltips={hideTooltips}
|
hideTooltips={hideTooltips}
|
||||||
/>
|
/>
|
||||||
<CPopover content={t('common.edit')}>
|
<CPopover content={t('common.details')}>
|
||||||
<CButton
|
<CButton
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -128,7 +128,7 @@ const LocationTable = ({
|
|||||||
onClick={() => toggleEditModal(item.id)}
|
onClick={() => toggleEditModal(item.id)}
|
||||||
style={{ width: '33px', height: '30px' }}
|
style={{ width: '33px', height: '30px' }}
|
||||||
>
|
>
|
||||||
<CIcon name="cil-pencil" content={cilPencil} size="sm" />
|
<CIcon name="cil-magnifying-glass" content={cilMagnifyingGlass} size="sm" />
|
||||||
</CButton>
|
</CButton>
|
||||||
</CPopover>
|
</CPopover>
|
||||||
</CButtonToolbar>
|
</CButtonToolbar>
|
||||||
|
|||||||
273
src/components/SimulationTable/index.js
Normal file
273
src/components/SimulationTable/index.js
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ReactPaginate from 'react-paginate';
|
||||||
|
import {
|
||||||
|
CCardBody,
|
||||||
|
CDataTable,
|
||||||
|
CButton,
|
||||||
|
CCard,
|
||||||
|
CCardHeader,
|
||||||
|
CRow,
|
||||||
|
CCol,
|
||||||
|
CPopover,
|
||||||
|
CSelect,
|
||||||
|
CButtonToolbar,
|
||||||
|
CButtonClose,
|
||||||
|
} from '@coreui/react';
|
||||||
|
import { cilTrash, cilSearch, cilMediaPlay } from '@coreui/icons';
|
||||||
|
import CIcon from '@coreui/icons-react';
|
||||||
|
import ReactTooltip from 'react-tooltip';
|
||||||
|
import { v4 as createUuid } from 'uuid';
|
||||||
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
|
const SimulationTable = ({
|
||||||
|
currentPage,
|
||||||
|
simulations,
|
||||||
|
simulationsPerPage,
|
||||||
|
loading,
|
||||||
|
updateSimulationsPerPage,
|
||||||
|
pageCount,
|
||||||
|
updatePage,
|
||||||
|
t,
|
||||||
|
deleteSimulation,
|
||||||
|
toggleEdit,
|
||||||
|
startSim,
|
||||||
|
}) => {
|
||||||
|
const columns = [
|
||||||
|
{ key: 'name', label: t('user.name'), filter: false, sorter: false, _style: { width: '10%' } },
|
||||||
|
{
|
||||||
|
key: 'gateway',
|
||||||
|
label: t('simulation.gateway'),
|
||||||
|
filter: false,
|
||||||
|
sorter: false,
|
||||||
|
_style: { width: '10%' },
|
||||||
|
},
|
||||||
|
{ key: 'deviceType', label: t('firmware.device_type'), filter: false, sorter: false },
|
||||||
|
{ key: 'devices', label: t('common.devices'), filter: false, sorter: false },
|
||||||
|
{ key: 'macPrefix', label: t('simulation.mac_prefix'), filter: false, sorter: false },
|
||||||
|
{ key: 'stateInterval', label: t('simulation.state_interval'), filter: false, sorter: false },
|
||||||
|
{
|
||||||
|
key: 'minAssociations',
|
||||||
|
label: t('simulation.min_associations'),
|
||||||
|
filter: false,
|
||||||
|
sorter: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'maxAssociations',
|
||||||
|
label: t('simulation.max_associations'),
|
||||||
|
filter: false,
|
||||||
|
sorter: false,
|
||||||
|
},
|
||||||
|
{ key: 'minClients', label: t('simulation.min_clients'), filter: false, sorter: false },
|
||||||
|
{ key: 'maxClients', label: t('simulation.max_clients'), filter: false, sorter: false },
|
||||||
|
{ key: 'simulationLength', label: t('simulation.length'), filter: false, sorter: false },
|
||||||
|
{ key: 'actions', label: '', filter: false, sorter: false, _style: { width: '1%' } },
|
||||||
|
];
|
||||||
|
|
||||||
|
const hideTooltips = () => ReactTooltip.hide();
|
||||||
|
|
||||||
|
const escFunction = (event) => {
|
||||||
|
if (event.keyCode === 27) {
|
||||||
|
hideTooltips();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener('keydown', escFunction, false);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', escFunction, false);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const deleteButton = ({ name, id }) => {
|
||||||
|
const tooltipId = createUuid();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CPopover content={t('common.delete')}>
|
||||||
|
<div>
|
||||||
|
<CButton
|
||||||
|
color="primary"
|
||||||
|
variant="outline"
|
||||||
|
shape="square"
|
||||||
|
size="sm"
|
||||||
|
className="mx-1"
|
||||||
|
data-tip
|
||||||
|
data-for={tooltipId}
|
||||||
|
data-event="click"
|
||||||
|
style={{ width: '33px', height: '30px' }}
|
||||||
|
>
|
||||||
|
<CIcon name="cil-trash" content={cilTrash} size="sm" />
|
||||||
|
</CButton>
|
||||||
|
<ReactTooltip
|
||||||
|
id={tooltipId}
|
||||||
|
place="top"
|
||||||
|
effect="solid"
|
||||||
|
globalEventOff="click"
|
||||||
|
clickable
|
||||||
|
className={[styles.deleteTooltip, 'tooltipRight'].join(' ')}
|
||||||
|
border
|
||||||
|
borderColor="#321fdb"
|
||||||
|
arrowColor="white"
|
||||||
|
overridePosition={({ left, top }) => {
|
||||||
|
const element = document.getElementById(tooltipId);
|
||||||
|
const tooltipWidth = element ? element.offsetWidth : 0;
|
||||||
|
const newLeft = left - tooltipWidth * 0.25;
|
||||||
|
return { top, left: newLeft };
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CCardHeader color="primary" className={styles.tooltipHeader}>
|
||||||
|
{t('simulation.delete_simulation', { name })}
|
||||||
|
<CButtonClose
|
||||||
|
style={{ color: 'white' }}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.target.parentNode.parentNode.classList.remove('show');
|
||||||
|
hideTooltips();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</CCardHeader>
|
||||||
|
<CCardBody>
|
||||||
|
<CRow>
|
||||||
|
<CCol>
|
||||||
|
<CButton
|
||||||
|
data-toggle="dropdown"
|
||||||
|
variant="outline"
|
||||||
|
color="danger"
|
||||||
|
onClick={() => deleteSimulation(id)}
|
||||||
|
block
|
||||||
|
>
|
||||||
|
{t('common.confirm')}
|
||||||
|
</CButton>
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
</CCardBody>
|
||||||
|
</ReactTooltip>
|
||||||
|
</div>
|
||||||
|
</CPopover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CCard className="m-0 p-0">
|
||||||
|
<CCardBody className="p-0">
|
||||||
|
<CDataTable
|
||||||
|
addTableClasses="ignore-overflow table-sm"
|
||||||
|
items={simulations ?? []}
|
||||||
|
fields={columns}
|
||||||
|
hover
|
||||||
|
border
|
||||||
|
loading={loading}
|
||||||
|
scopedSlots={{
|
||||||
|
name: (item) => <td className="align-middle">{item.name}</td>,
|
||||||
|
gateway: (item) => <td className="align-middle">{item.gateway}</td>,
|
||||||
|
deviceType: (item) => <td className="align-middle">{item.deviceType}</td>,
|
||||||
|
devices: (item) => <td className="align-middle">{item.devices}</td>,
|
||||||
|
macPrefix: (item) => <td className="align-middle">{item.macPrefix}</td>,
|
||||||
|
healthCheckInterval: (item) => (
|
||||||
|
<td className="align-middle">{item.healthCheckInterval}</td>
|
||||||
|
),
|
||||||
|
stateInterval: (item) => <td className="align-middle">{item.stateInterval}</td>,
|
||||||
|
minAssociations: (item) => <td className="align-middle">{item.minAssociations}</td>,
|
||||||
|
maxAssociations: (item) => <td className="align-middle">{item.maxAssociations}</td>,
|
||||||
|
minClients: (item) => <td className="align-middle">{item.minClients}</td>,
|
||||||
|
maxClients: (item) => <td className="align-middle">{item.maxClients}</td>,
|
||||||
|
simulationLength: (item) => <td className="align-middle">{item.simulationLength}</td>,
|
||||||
|
actions: (item) => (
|
||||||
|
<td className="text-center align-middle">
|
||||||
|
<CButtonToolbar
|
||||||
|
role="group"
|
||||||
|
className="justify-content-center"
|
||||||
|
style={{ width: '150px' }}
|
||||||
|
>
|
||||||
|
<CPopover content={t('simulation.run_simulation')}>
|
||||||
|
<CButton
|
||||||
|
color="primary"
|
||||||
|
variant="outline"
|
||||||
|
shape="square"
|
||||||
|
size="sm"
|
||||||
|
className="mx-1"
|
||||||
|
style={{ width: '33px', height: '30px' }}
|
||||||
|
onClick={() => startSim(item.id)}
|
||||||
|
>
|
||||||
|
<CIcon name="cil-media-play" content={cilMediaPlay} size="sm" />
|
||||||
|
</CButton>
|
||||||
|
</CPopover>
|
||||||
|
{deleteButton(item)}
|
||||||
|
<CPopover content={t('configuration.details')}>
|
||||||
|
<CButton
|
||||||
|
color="primary"
|
||||||
|
variant="outline"
|
||||||
|
shape="square"
|
||||||
|
size="sm"
|
||||||
|
className="mx-1"
|
||||||
|
style={{ width: '33px', height: '30px' }}
|
||||||
|
onClick={() => toggleEdit(item.id)}
|
||||||
|
>
|
||||||
|
<CIcon name="cil-search" content={cilSearch} size="sm" />
|
||||||
|
</CButton>
|
||||||
|
</CPopover>
|
||||||
|
</CButtonToolbar>
|
||||||
|
</td>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="d-flex flex-row pl-3">
|
||||||
|
<div className="pr-3">
|
||||||
|
<ReactPaginate
|
||||||
|
previousLabel="← Previous"
|
||||||
|
nextLabel="Next →"
|
||||||
|
pageCount={pageCount}
|
||||||
|
onPageChange={updatePage}
|
||||||
|
forcePage={Number(currentPage)}
|
||||||
|
breakClassName="page-item"
|
||||||
|
breakLinkClassName="page-link"
|
||||||
|
containerClassName="pagination"
|
||||||
|
pageClassName="page-item"
|
||||||
|
pageLinkClassName="page-link"
|
||||||
|
previousClassName="page-item"
|
||||||
|
previousLinkClassName="page-link"
|
||||||
|
nextClassName="page-item"
|
||||||
|
nextLinkClassName="page-link"
|
||||||
|
activeClassName="active"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="pr-2 mt-1">{t('common.items_per_page')}</p>
|
||||||
|
<div style={{ width: '100px' }} className="px-2">
|
||||||
|
<CSelect
|
||||||
|
custom
|
||||||
|
defaultValue={simulationsPerPage}
|
||||||
|
onChange={(e) => updateSimulationsPerPage(e.target.value)}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="25">25</option>
|
||||||
|
<option value="50">50</option>
|
||||||
|
</CSelect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CCardBody>
|
||||||
|
</CCard>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SimulationTable.propTypes = {
|
||||||
|
startSim: PropTypes.func.isRequired,
|
||||||
|
currentPage: PropTypes.string,
|
||||||
|
simulations: PropTypes.instanceOf(Array).isRequired,
|
||||||
|
updateSimulationsPerPage: PropTypes.func.isRequired,
|
||||||
|
pageCount: PropTypes.number.isRequired,
|
||||||
|
updatePage: PropTypes.func.isRequired,
|
||||||
|
simulationsPerPage: PropTypes.string.isRequired,
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
|
loading: PropTypes.bool.isRequired,
|
||||||
|
toggleEdit: PropTypes.func.isRequired,
|
||||||
|
deleteSimulation: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
SimulationTable.defaultProps = {
|
||||||
|
currentPage: '0',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(SimulationTable);
|
||||||
16
src/components/SimulationTable/index.module.scss
Normal file
16
src/components/SimulationTable/index.module.scss
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
.deleteTooltip {
|
||||||
|
opacity: 1 !important;
|
||||||
|
padding: 0px 0px 0px 0px !important;
|
||||||
|
border-radius: 1rem !important;
|
||||||
|
background-color: #fff !important;
|
||||||
|
border-color: #321fdb !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
font-weight: 400 !important;
|
||||||
|
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2) !important;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltipHeader {
|
||||||
|
border-top-left-radius: 1rem !important;
|
||||||
|
border-top-right-radius: 1rem !important;
|
||||||
|
}
|
||||||
@@ -169,6 +169,7 @@ const UserListTable = ({
|
|||||||
onClick={() => handleDeleteClick(item.Id)}
|
onClick={() => handleDeleteClick(item.Id)}
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
shape="square"
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
<CIcon content={cilTrash} size="sm" />
|
<CIcon content={cilTrash} size="sm" />
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export { default as AddEntityForm } from './components/AddEntityForm';
|
|||||||
export { default as AddInventoryTagForm } from './components/AddInventoryTagForm';
|
export { default as AddInventoryTagForm } from './components/AddInventoryTagForm';
|
||||||
export { default as AddLocationForm } from './components/AddLocationForm';
|
export { default as AddLocationForm } from './components/AddLocationForm';
|
||||||
export { default as AddressEditor } from './components/AddressEditor';
|
export { default as AddressEditor } from './components/AddressEditor';
|
||||||
|
export { default as AddSimulationForm } from './components/AddSimulationForm';
|
||||||
export { default as ApiStatusCard } from './components/ApiStatusCard';
|
export { default as ApiStatusCard } from './components/ApiStatusCard';
|
||||||
export { default as Avatar } from './components/Avatar';
|
export { default as Avatar } from './components/Avatar';
|
||||||
export { default as CompactNotesTable } from './components/CompactNotesTable';
|
export { default as CompactNotesTable } from './components/CompactNotesTable';
|
||||||
@@ -29,6 +30,7 @@ export { default as ConfigurationSelect } from './components/Configuration/Selec
|
|||||||
export { default as ConfigurationStringField } from './components/Configuration/StringField';
|
export { default as ConfigurationStringField } from './components/Configuration/StringField';
|
||||||
export { default as ConfigurationToggle } from './components/Configuration/Toggle';
|
export { default as ConfigurationToggle } from './components/Configuration/Toggle';
|
||||||
export { default as ConfirmFooter } from './components/ConfirmFooter';
|
export { default as ConfirmFooter } from './components/ConfirmFooter';
|
||||||
|
export { default as ConfirmStopEditingButton } from './components/ConfirmStopEditingButton';
|
||||||
export { default as ContactTable } from './components/ContactTable';
|
export { default as ContactTable } from './components/ContactTable';
|
||||||
export { default as CopyToClipboardButton } from './components/CopyToClipboardButton';
|
export { default as CopyToClipboardButton } from './components/CopyToClipboardButton';
|
||||||
export { default as CreateUserForm } from './components/CreateUserForm';
|
export { default as CreateUserForm } from './components/CreateUserForm';
|
||||||
@@ -45,8 +47,10 @@ export { default as EditEntityForm } from './components/EditEntityForm';
|
|||||||
export { default as EditInventoryTagForm } from './components/EditInventoryTagForm';
|
export { default as EditInventoryTagForm } from './components/EditInventoryTagForm';
|
||||||
export { default as EditLocationForm } from './components/EditLocationForm';
|
export { default as EditLocationForm } from './components/EditLocationForm';
|
||||||
export { default as EditMyProfile } from './components/EditMyProfile';
|
export { default as EditMyProfile } from './components/EditMyProfile';
|
||||||
|
export { default as EditSimulationForm } from './components/EditSimulationForm';
|
||||||
export { default as EditUserForm } from './components/EditUserForm';
|
export { default as EditUserForm } from './components/EditUserForm';
|
||||||
export { default as EditUserModal } from './components/EditUserModal';
|
export { default as EditUserModal } from './components/EditUserModal';
|
||||||
|
export { default as EntityTree } from './components/EntityTree';
|
||||||
export { default as EventQueueModal } from './components/EventQueueModal';
|
export { default as EventQueueModal } from './components/EventQueueModal';
|
||||||
export { default as InventoryTable } from './components/InventoryTable';
|
export { default as InventoryTable } from './components/InventoryTable';
|
||||||
export { default as FileToStringButton } from './components/FileToStringButton';
|
export { default as FileToStringButton } from './components/FileToStringButton';
|
||||||
@@ -62,6 +66,7 @@ export { default as LoadingButton } from './components/LoadingButton';
|
|||||||
export { default as NetworkDiagram } from './components/NetworkDiagram';
|
export { default as NetworkDiagram } from './components/NetworkDiagram';
|
||||||
export { default as NotesTable } from './components/NotesTable';
|
export { default as NotesTable } from './components/NotesTable';
|
||||||
export { default as RadioAnalysisTable } from './components/RadioAnalysisTable';
|
export { default as RadioAnalysisTable } from './components/RadioAnalysisTable';
|
||||||
|
export { default as SimulationTable } from './components/SimulationTable';
|
||||||
export { default as UserListTable } from './components/UserListTable';
|
export { default as UserListTable } from './components/UserListTable';
|
||||||
export { default as VenueTable } from './components/VenueTable';
|
export { default as VenueTable } from './components/VenueTable';
|
||||||
export { default as WifiAnalysisTable } from './components/WifiAnalysisTable';
|
export { default as WifiAnalysisTable } from './components/WifiAnalysisTable';
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ const Header = ({
|
|||||||
user,
|
user,
|
||||||
avatar,
|
avatar,
|
||||||
hideBreadcrumb,
|
hideBreadcrumb,
|
||||||
|
extraButton,
|
||||||
|
hideSidebarButton,
|
||||||
}) => {
|
}) => {
|
||||||
const [translatedRoutes, setTranslatedRoutes] = useState(routes);
|
const [translatedRoutes, setTranslatedRoutes] = useState(routes);
|
||||||
|
|
||||||
@@ -49,14 +51,25 @@ const Header = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<CHeader withSubheader>
|
<CHeader withSubheader>
|
||||||
|
{hideSidebarButton ? null : (
|
||||||
|
<>
|
||||||
<CToggler inHeader className="ml-md-3 d-lg-none" onClick={toggleSidebarMobile} />
|
<CToggler inHeader className="ml-md-3 d-lg-none" onClick={toggleSidebarMobile} />
|
||||||
<CToggler inHeader className="ml-3 d-md-down-none" onClick={toggleSidebar} />
|
<CToggler inHeader className="ml-3 d-md-down-none" onClick={toggleSidebar} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<CHeaderBrand className="mx-auto d-lg-none" to="/">
|
<CHeaderBrand className="mx-auto d-lg-none" to="/">
|
||||||
<img src={logo} alt="OpenWifi" />
|
<img
|
||||||
|
src={logo}
|
||||||
|
alt="OpenWifi"
|
||||||
|
className="c-sidebar-brand-full"
|
||||||
|
style={{ height: '75px', width: '175px' }}
|
||||||
|
/>
|
||||||
</CHeaderBrand>
|
</CHeaderBrand>
|
||||||
|
|
||||||
<CHeaderNav className="d-md-down-none mr-auto" />
|
<CHeaderNav className="d-md-down-none mr-auto" />
|
||||||
|
|
||||||
|
<CHeaderNav>{extraButton}</CHeaderNav>
|
||||||
|
|
||||||
<CHeaderNav className="px-3">
|
<CHeaderNav className="px-3">
|
||||||
<LanguageSwitcher i18n={i18n} />
|
<LanguageSwitcher i18n={i18n} />
|
||||||
</CHeaderNav>
|
</CHeaderNav>
|
||||||
@@ -78,12 +91,14 @@ const Header = ({
|
|||||||
</CDropdown>
|
</CDropdown>
|
||||||
</CHeaderNav>
|
</CHeaderNav>
|
||||||
|
|
||||||
|
{hideBreadcrumb ? null : (
|
||||||
<CSubheader hidden={hideBreadcrumb} className="px-3 justify-content-between">
|
<CSubheader hidden={hideBreadcrumb} className="px-3 justify-content-between">
|
||||||
<CBreadcrumbRouter
|
<CBreadcrumbRouter
|
||||||
className="border-0 c-subheader-nav m-0 px-0 px-md-3"
|
className="border-0 c-subheader-nav m-0 px-0 px-md-3"
|
||||||
routes={translatedRoutes}
|
routes={translatedRoutes}
|
||||||
/>
|
/>
|
||||||
</CSubheader>
|
</CSubheader>
|
||||||
|
)}
|
||||||
</CHeader>
|
</CHeader>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -101,10 +116,14 @@ Header.propTypes = {
|
|||||||
user: PropTypes.instanceOf(Object).isRequired,
|
user: PropTypes.instanceOf(Object).isRequired,
|
||||||
avatar: PropTypes.string.isRequired,
|
avatar: PropTypes.string.isRequired,
|
||||||
hideBreadcrumb: PropTypes.bool,
|
hideBreadcrumb: PropTypes.bool,
|
||||||
|
extraButton: PropTypes.node,
|
||||||
|
hideSidebarButton: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
Header.defaultProps = {
|
Header.defaultProps = {
|
||||||
|
extraButton: null,
|
||||||
hideBreadcrumb: false,
|
hideBreadcrumb: false,
|
||||||
|
hideSidebarButton: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(Header);
|
export default React.memo(Header);
|
||||||
|
|||||||
@@ -13,16 +13,26 @@ import {
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
const Sidebar = ({ showSidebar, setShowSidebar, logo, options, redirectTo }) => (
|
const Sidebar = ({
|
||||||
|
showSidebar,
|
||||||
|
setShowSidebar,
|
||||||
|
logo,
|
||||||
|
options,
|
||||||
|
redirectTo,
|
||||||
|
logoHeight,
|
||||||
|
logoWidth,
|
||||||
|
}) => (
|
||||||
<CSidebar show={showSidebar} onShowChange={(val) => setShowSidebar(val)}>
|
<CSidebar show={showSidebar} onShowChange={(val) => setShowSidebar(val)}>
|
||||||
<CSidebarBrand className="d-md-down-none" to={redirectTo}>
|
<CSidebarBrand className="d-md-down-none" to={redirectTo}>
|
||||||
<img
|
<img
|
||||||
className={[styles.sidebarImgFull, 'c-sidebar-brand-full'].join(' ')}
|
className={[styles.sidebarImgFull, 'c-sidebar-brand-full'].join(' ')}
|
||||||
|
style={{ height: logoHeight ?? undefined, width: logoWidth ?? undefined }}
|
||||||
src={logo}
|
src={logo}
|
||||||
alt="OpenWifi"
|
alt="OpenWifi"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
className={[styles.sidebarImgMinimized, 'c-sidebar-brand-minimized'].join(' ')}
|
className={[styles.sidebarImgMinimized, 'c-sidebar-brand-minimized'].join(' ')}
|
||||||
|
style={{ height: logoHeight ?? undefined, width: logoWidth ?? undefined }}
|
||||||
src={logo}
|
src={logo}
|
||||||
alt="OpenWifi"
|
alt="OpenWifi"
|
||||||
/>
|
/>
|
||||||
@@ -48,6 +58,13 @@ Sidebar.propTypes = {
|
|||||||
logo: PropTypes.string.isRequired,
|
logo: PropTypes.string.isRequired,
|
||||||
options: PropTypes.arrayOf(Object).isRequired,
|
options: PropTypes.arrayOf(Object).isRequired,
|
||||||
redirectTo: PropTypes.string.isRequired,
|
redirectTo: PropTypes.string.isRequired,
|
||||||
|
logoHeight: PropTypes.string,
|
||||||
|
logoWidth: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
Sidebar.defaultProps = {
|
||||||
|
logoHeight: null,
|
||||||
|
logoWidth: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(Sidebar);
|
export default React.memo(Sidebar);
|
||||||
|
|||||||
Reference in New Issue
Block a user