Waiting for trace

This commit is contained in:
bourquecharles
2021-06-25 09:35:47 -04:00
parent d8b501fff0
commit e75c163248
16 changed files with 468 additions and 257 deletions

View File

@@ -19,7 +19,7 @@
},
"commands": {
"error": "Fehler beim Senden des Befehls!",
"success": "Befehl erfolgreich übermittelt",
"success": "Der Befehl wurde erfolgreich gesendet. Sie können das Ergebnis im Befehlsprotokoll anzeigen",
"title": "Gerätebefehle"
},
"common": {
@@ -160,8 +160,10 @@
"trace": {
"choose_network": "Netzwerk auswählen",
"directions": "Starten Sie eine Fernverfolgung dieses Geräts für eine bestimmte Dauer oder eine Anzahl von Paketen",
"download_trace": "Klicke hier zum herunterladen",
"packets": "Pakete",
"title": "Trace-Gerät"
"title": "Trace-Gerät",
"waiting_seconds": "Verstrichene Zeit: {{seconds}} Sekunden"
},
"upgrade": {
"command_submitted": "Befehl gesendet",

View File

@@ -19,7 +19,7 @@
},
"commands": {
"error": "Error while submitting command!",
"success": "Command submitted successfully",
"success": "Command submitted successfully, you can look at the Commands log for the result",
"title": "Device Commands"
},
"common": {
@@ -160,8 +160,10 @@
"trace": {
"choose_network": "Choose network",
"directions": "Launch a remote trace of this device for either a specific duration or a number of packets",
"download_trace": "Click here to download",
"packets": "Packets",
"title": "Trace Device"
"title": "Trace Device",
"waiting_seconds": "Time Elapsed: {{seconds}} seconds"
},
"upgrade": {
"command_submitted": "Command submitted",

View File

@@ -19,7 +19,7 @@
},
"commands": {
"error": "¡Error al enviar el comando!",
"success": "Comando enviado con éxito",
"success": "Comando enviado con éxito, puede consultar el registro de Comandos para ver el resultado",
"title": "Comandos del dispositivo"
},
"common": {
@@ -160,8 +160,10 @@
"trace": {
"choose_network": "Elija la red",
"directions": "Lanzar un rastreo remoto de este dispositivo por una duración específica o por una cantidad de paquetes",
"download_trace": "Haga click aquí para descargar",
"packets": "Paquetes",
"title": "Dispositivo de seguimiento"
"title": "Dispositivo de seguimiento",
"waiting_seconds": "Tiempo transcurrido: {{seconds}} segundos"
},
"upgrade": {
"command_submitted": "Comando enviado",

View File

@@ -19,7 +19,7 @@
},
"commands": {
"error": "Erreur lors de la soumission de la commande !",
"success": "Commande soumise avec succès",
"success": "Commande soumise avec succès, vous pouvez consulter le journal des commandes pour le résultat",
"title": "Commandes de l'appareil"
},
"common": {
@@ -160,8 +160,10 @@
"trace": {
"choose_network": "Choisir le réseau",
"directions": "Lancer une trace à distance de cet appareil pour une durée spécifique ou un nombre de paquets",
"download_trace": "Cliquez ici pour télécharger",
"packets": "Paquets",
"title": "Dispositif de traçage"
"title": "Dispositif de traçage",
"waiting_seconds": "Temps écoulé : {{seconds}} secondes"
},
"upgrade": {
"command_submitted": "Commande soumise",

View File

@@ -19,7 +19,7 @@
},
"commands": {
"error": "Erro ao enviar comando!",
"success": "Comando enviado com sucesso",
"success": "Comando enviado com sucesso, você pode consultar o log de Comandos para ver o resultado",
"title": "Comandos de dispositivo"
},
"common": {
@@ -160,8 +160,10 @@
"trace": {
"choose_network": "Escolha a rede",
"directions": "Lançar um rastreamento remoto deste dispositivo para uma duração específica ou um número de pacotes",
"download_trace": "Clique aqui para baixar",
"packets": "Pacotes",
"title": "Dispositivo de rastreamento"
"title": "Dispositivo de rastreamento",
"waiting_seconds": "Tempo decorrido: {{seconds}} segundos"
},
"upgrade": {
"command_submitted": "Comando enviado",

View File

@@ -24,7 +24,7 @@ const UpgradeWaitingBody = ({ serialNumber }) => {
.get(`/device/${encodeURIComponent(serialNumber)}/status`, options)
.then((response) => response.data.connected)
.catch(() => {});
}
};
const getFirmwareVersion = () => {
const options = {
@@ -38,28 +38,27 @@ const UpgradeWaitingBody = ({ serialNumber }) => {
.get(`/device/${encodeURIComponent(serialNumber)}`, options)
.then((response) => response.data.firmware)
.catch(() => {});
}
};
const refreshStep = () => {
if(currentStep === 0 && !getDeviceConnection){
if (currentStep === 0 && !getDeviceConnection) {
const labelsToAdd = [
t('upgrade.device_disconnected'),
t('upgrade.device_upgrading_firmware'),
t('upgrade.waiting_for_device'),
];
setLabelsToShow([...labelsToShow, ...labelsToAdd])
setLabelsToShow([...labelsToShow, ...labelsToAdd]);
setCurrentStep(1);
}
else if(currentStep === 1 && getDeviceConnection()){
} else if (currentStep === 1 && getDeviceConnection()) {
const newFirmware = `: ${getFirmwareVersion()}`;
const labelsToAdd = [
t('upgrade.device_reconnected'),
`${t('upgrade.new_version')}: ${newFirmware}`
`${t('upgrade.new_version')}: ${newFirmware}`,
];
setLabelsToShow([...labelsToShow, ...labelsToAdd]);
setCurrentStep(2);
}
}
};
useEffect(() => {
const refreshIntervalId = setInterval(() => {
@@ -68,7 +67,7 @@ const UpgradeWaitingBody = ({ serialNumber }) => {
const timerIntervalId = setInterval(() => {
setSecondsElapsed(secondsElapsed + 1);
})
}, 1000);
return () => {
clearInterval(refreshIntervalId);
@@ -79,10 +78,14 @@ const UpgradeWaitingBody = ({ serialNumber }) => {
return (
<CModalBody>
<div className="consoleBox">
{
labelsToShow.map((label) => <p key={createUuid()}>{new Date().toString()}: {label}</p>)
}
<p>{t('common.seconds_elapsed')}: {secondsElapsed}</p>
{labelsToShow.map((label) => (
<p key={createUuid()}>
{new Date().toString()}: {label}
</p>
))}
<p>
{t('common.seconds_elapsed')}: {secondsElapsed}
</p>
</div>
</CModalBody>
);

View File

@@ -9,7 +9,7 @@ import {
CRow,
CInput,
CInvalidFeedback,
CModalFooter
CModalFooter,
} from '@coreui/react';
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
@@ -21,9 +21,10 @@ import 'react-widgets/styles.css';
import { getToken } from 'utils/authHelper';
import axiosInstance from 'utils/axiosInstance';
import eventBus from 'utils/eventBus';
import ButtonFooter from './containers/UpgradeFooter';
import getDeviceConnection from 'utils/deviceHelper';
import ButtonFooter from './UpgradeFooter';
import styles from './index.module.scss';
import UpgradeWaitingBody from './containers/UpgradeWaitingBody';
import UpgradeWaitingBody from './UpgradeWaitingBody';
const FirmwareUpgradeModal = ({ show, toggleModal }) => {
const { t } = useTranslation();
@@ -37,10 +38,18 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
const [disabledWaiting, setDisableWaiting] = useState(false);
const [waitingForUpgrade, setWaitingForUpgrade] = useState(false);
const [showWaitingConsole, setShowWaitingConsole] = useState(false);
const [lastResult, setLastResult] = useState(null);
const [deviceConnected, setDeviceConnected] = useState(true);
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
const toggleNow = () => {
if(isNow){
setWaitForUpgrade(false);
setDisableWaiting(true);
}
else{
setDisableWaiting(false);
}
setIsNow(!isNow);
};
@@ -48,20 +57,6 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
setWaitForUpgrade(waitForUpgrade);
};
const getDeviceConnection = async () => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${getToken()}`,
},
};
return axiosInstance
.get(`/device/${encodeURIComponent(selectedDeviceId)}/status`, options)
.then((response) => response.data.connected)
.catch(() => false);
}
const formValidation = () => {
let valid = true;
if (firmware.trim() === '') {
@@ -87,11 +82,12 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
}, [firmware, date]);
useEffect(() => {
if(selectedDeviceId !== null && show){
if (selectedDeviceId !== null && show) {
const asyncGet = async () => {
const isConnected = await getDeviceConnection();
const isConnected = await getDeviceConnection(selectedDeviceId);
setDisableWaiting(!isConnected);
}
setDeviceConnected(isConnected);
};
asyncGet();
}
}, [show]);
@@ -112,18 +108,16 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
uri: firmware,
};
axiosInstance
.post(`/device/${encodeURIComponent(selectedDeviceId)}/upgrade`, parameters, { headers })
.then((response) => {
setLastResult(response);
if(waitForUpgrade) {
.post(`/device/${encodeURIComponent(selectedDeviceId)}/reboot`, parameters, { headers })
.then(() => {
if (waitForUpgrade) {
console.log('waiting');
setShowWaitingConsole(true);
}
})
.catch(() => {
setLastResult('error');
})
.finally(() => {
console.log(lastResult);
setBlockFields(false);
setWaitingForUpgrade(false);
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
@@ -131,16 +125,14 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
}
};
if(showWaitingConsole){
if (showWaitingConsole) {
return (
<CModal show={show} onClose={toggleModal}>
<CModalHeader closeButton>
<CModalTitle>{t('upgrade.title')}</CModalTitle>
</CModalHeader>
<CModalBody>
<UpgradeWaitingBody
serialNumber={selectedDeviceId}
/>
<UpgradeWaitingBody serialNumber={selectedDeviceId} />
</CModalBody>
<CModalFooter>
<CButton color="secondary" onClick={toggleModal}>
@@ -158,37 +150,6 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
</CModalHeader>
<CModalBody>
<h6>{t('upgrade.directions')}</h6>
<CRow className={styles.spacedRow}>
<CCol md="8">
<p className={styles.spacedText}>{t('common.execute_now')}</p>
</CCol>
<CCol>
<CSwitch
disabled={blockFields}
color="primary"
defaultChecked={isNow}
onClick={toggleNow}
labelOn={t('common.yes')}
labelOff={t('common.no')}
/>
</CCol>
</CRow>
<CRow hidden={isNow}>
<CCol md="4" className={styles.spacedColumn}>
<p>{t('upgrade.time')}</p>
</CCol>
<CCol xs="12" md="8">
<DatePicker
selected={new Date(date)}
value={new Date(date)}
className={('form-control', { 'is-invalid': !validDate })}
includeTime
disabled={blockFields}
onChange={(newDate) => setDate(newDate.toString())}
/>
<CInvalidFeedback>{t('common.need_date')}</CInvalidFeedback>
</CCol>
</CRow>
<CRow className={styles.spacedRow}>
<CCol md="4" className={styles.spacedColumn}>
<p>{t('upgrade.firmware_uri')}</p>
@@ -209,7 +170,41 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
</CRow>
<CRow className={styles.spacedRow}>
<CCol md="8">
<p className={styles.spacedText}>{t('upgrade.wait_for_upgrade')}<b hidden={!disabledWaiting}> {t('upgrade.offline_device')}</b></p>
<p className={styles.spacedText}>{t('common.execute_now')}</p>
</CCol>
<CCol>
<CSwitch
disabled={blockFields}
color="primary"
defaultChecked={isNow}
onClick={toggleNow}
labelOn={t('common.yes')}
labelOff={t('common.no')}
/>
</CCol>
</CRow>
<CRow className={styles.spacedRow} hidden={isNow}>
<CCol md="4" className={styles.spacedColumn}>
<p>{t('upgrade.time')}</p>
</CCol>
<CCol xs="12" md="8">
<DatePicker
selected={new Date(date)}
value={new Date(date)}
className={('form-control', { 'is-invalid': !validDate })}
includeTime
disabled={blockFields}
onChange={(newDate) => setDate(newDate.toString())}
/>
<CInvalidFeedback>{t('common.need_date')}</CInvalidFeedback>
</CCol>
</CRow>
<CRow className={styles.spacedRow} hidden={!isNow || disabledWaiting || !deviceConnected}>
<CCol md="8">
<p className={styles.spacedText}>
{t('upgrade.wait_for_upgrade')}
<b hidden={!disabledWaiting}> {t('upgrade.offline_device')}</b>
</p>
</CCol>
<CCol>
<CSwitch

View File

@@ -1,4 +1,4 @@
import React, {useState, useEffect} from 'react';
import React, { useState, useEffect } from 'react';
import {
CButton,
CModal,
@@ -30,11 +30,11 @@ const LatestStatisticsModal = ({ show, toggle, serialNumber }) => {
.then((response) => {
setLatestStats(response.data);
})
.catch(() => {})
}
.catch(() => {});
};
useEffect(() => {
if(show){
if (show) {
getLatestStats();
}
}, [show]);
@@ -54,7 +54,7 @@ const LatestStatisticsModal = ({ show, toggle, serialNumber }) => {
</CModalFooter>
</CModal>
);
}
};
LatestStatisticsModal.propTypes = {
serialNumber: PropTypes.string.isRequired,

View File

@@ -60,14 +60,15 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
// Looping through the interfaces of the log
for (const inter of log.data.interfaces) {
interfaceList[interfaceTypes[inter.name]][0].data.push(
Math.floor(inter.counters.tx_bytes / 1024),
inter.counters?.tx_bytes? Math.floor(inter.counters.tx_bytes / 1024) : 0
);
interfaceList[interfaceTypes[inter.name]][1].data.push(
Math.floor(inter.counters.rx_bytes / 1024),
inter.counters?.rx_bytes? Math.floor(inter.counters.rx_bytes / 1024) : 0
);
}
}
const options = {
chart: {
id: 'chart',
@@ -105,7 +106,6 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
interfaceList,
settings: options,
};
if (statOptions !== newOptions) {
setStatOptions(newOptions);
}

View File

@@ -1,7 +1,17 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import { CDropdown, CDropdownToggle, CDropdownMenu, CDropdownItem, CCard, CCardHeader, CCardBody, CRow, CCol } from '@coreui/react';
import {
CDropdown,
CDropdownToggle,
CDropdownMenu,
CDropdownItem,
CCard,
CCardHeader,
CCardBody,
CRow,
CCol,
} from '@coreui/react';
import { cilOptions } from '@coreui/icons';
import CIcon from '@coreui/icons-react';
import StatisticsChartList from './StatisticsChartList';
@@ -15,7 +25,7 @@ const DeviceStatisticsCard = ({ selectedDeviceId }) => {
const toggleLatestModal = () => {
setShowLatestModal(!showLatestModal);
}
};
const refresh = () => {
setLastRefresh(new Date().toString());
@@ -26,20 +36,19 @@ const DeviceStatisticsCard = ({ selectedDeviceId }) => {
<CCard>
<CCardHeader>
<CRow>
<CCol><h4>{t('statistics.title')}</h4></CCol>
<CCol>
<h4>{t('statistics.title')}</h4>
</CCol>
<CCol className={styles.alignRight}>
<CDropdown className="m-1 btn-group">
<CDropdownToggle>
<CIcon
name="cil-options"
content={cilOptions}
size="lg"
color="primary"
/>
<CIcon name="cil-options" content={cilOptions} size="lg" color="primary" />
</CDropdownToggle>
<CDropdownMenu>
<CDropdownItem onClick={refresh}>{t('common.refresh')}</CDropdownItem>
<CDropdownItem onClick={toggleLatestModal}>{t('statistics.show_latest')}</CDropdownItem>
<CDropdownItem onClick={toggleLatestModal}>
{t('statistics.show_latest')}
</CDropdownItem>
</CDropdownMenu>
</CDropdown>
</CCol>
@@ -49,7 +58,11 @@ const DeviceStatisticsCard = ({ selectedDeviceId }) => {
<StatisticsChartList selectedDeviceId={selectedDeviceId} lastRefresh={lastRefresh} />
</CCardBody>
</CCard>
<LatestStatisticsModal show={showLatestModal} toggle={toggleLatestModal} serialNumber={selectedDeviceId}/>
<LatestStatisticsModal
show={showLatestModal}
toggle={toggleLatestModal}
serialNumber={selectedDeviceId}
/>
</div>
);
};

View File

@@ -0,0 +1,101 @@
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import { CModalBody, CButton, CSpinner } from '@coreui/react';
import { getToken } from 'utils/authHelper';
import axiosInstance from 'utils/axiosInstance';
import styles from './index.module.scss';
const WaitingForTraceBody = ({serialNumber, commandUuid}) => {
const { t } = useTranslation();
const [secondsElapsed, setSecondsElapsed] = useState(0);
const [waitingForFile, setWaitingForFile] = useState(true);
const getTraceResult = () => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${getToken()}`,
},
};
axiosInstance
.get(`/command/${encodeURIComponent(commandUuid)}`, options)
.then((response) => {
if(response.data.waitingForFile === 0){
setWaitingForFile(false);
}
})
.catch(() => {});
}
const downloadTrace = () => {
const options = {
headers: {
Accept: 'application/octet-stream',
Authorization: `Bearer ${getToken()}`,
},
responseType: 'arraybuffer',
};
axiosInstance
.get(`/file/${commandUuid}?serialNumber=${serialNumber}`, options)
.then((response) => {
const blob = new Blob([response.data], { type: 'application/octet-stream' });
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = `Trace_${commandUuid}.pcap`;
link.click();
});
}
useEffect(() => {
const timer = setInterval(() => {
setSecondsElapsed(secondsElapsed + 1);
}, 1000);
if(!waitingForFile){
clearInterval(timer);
}
return () => {
clearInterval(timer);
}
}, [waitingForFile, secondsElapsed]);
useEffect(() => {
const refreshStatus = setInterval(() => {
getTraceResult();
}, 5000);
if(!waitingForFile){
clearInterval(refreshStatus);
}
return () => {
clearInterval(refreshStatus);
}
}, [waitingForFile]);
return (
<CModalBody>
<p>{t('trace.waiting_seconds', {seconds: secondsElapsed})}</p>
<div className={styles.centerDiv}>
<CSpinner hidden={!waitingForFile} />
</div>
<CButton
hidden={waitingForFile}
onClick={downloadTrace}
disabled={waitingForFile}
color="link"
block
>
{t('trace.download_trace')}
</CButton>
</CModalBody>
);
}
WaitingForTraceBody.propTypes = {
serialNumber: PropTypes.string.isRequired,
commandUuid: PropTypes.string.isRequired,
};
export default WaitingForTraceBody;

View File

@@ -9,6 +9,7 @@ import {
CRow,
CInvalidFeedback,
CSelect,
CSwitch,
CForm,
CInputRadio,
CFormGroup,
@@ -19,28 +20,44 @@ import { useTranslation } from 'react-i18next';
import DatePicker from 'react-widgets/DatePicker';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { convertDateToUtc, dateToUnix } from 'utils/helper';
import { dateToUnix } from 'utils/helper';
import 'react-widgets/styles.css';
import { getToken } from 'utils/authHelper';
import axiosInstance from 'utils/axiosInstance';
import eventBus from 'utils/eventBus';
import getDeviceConnection from 'utils/deviceHelper';
import LoadingButton from 'components/LoadingButton';
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
import WaitingForTraceBody from './WaitingForTraceBody';
import styles from './index.module.scss';
const TraceModal = ({ show, toggleModal }) => {
const { t } = useTranslation();
const [hadSuccess, setHadSuccess] = useState(false);
const [hadFailure, setHadFailure] = useState(false);
const [waiting, setWaiting] = useState(false);
const [blockFields, setBlockFields] = useState(false);
const [usingDuration, setUsingDuration] = useState(true);
const [duration, setDuration] = useState(20);
const [packets, setPackets] = useState(100);
const [chosenDate, setChosenDate] = useState(new Date().toString());
const [responseBody, setResponseBody] = useState('');
const [chosenInterface, setChosenInterface] = useState('up');
const [isDeviceConnected, setIsDeviceConnected] = useState(false);
const [isNow, setIsNow] = useState(true);
const [waitForTrace, setWaitForTrace] = useState(false);
const [waitingForTrace, setWaitingForTrace] = useState(false);
const [commandUuid, setCommandUuid] = useState(null);
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
const toggleWaitForTrace = () => {
setWaitForTrace(!waitForTrace);
}
const toggleNow = () => {
setIsNow(!isNow);
}
const setDate = (date) => {
if (date) {
setChosenDate(date.toString());
@@ -48,35 +65,27 @@ const TraceModal = ({ show, toggleModal }) => {
};
useEffect(() => {
setWaitForTrace(false);
setHadSuccess(false);
setHadFailure(false);
setWaiting(false);
setChosenDate(new Date().toString());
setResponseBody('');
setDuration(20);
setPackets(100);
setChosenInterface('up');
setWaitingForTrace(false);
}, [show]);
const doAction = () => {
setBlockFields(true);
setHadFailure(false);
setHadSuccess(false);
setWaiting(true);
const token = getToken();
const dateChosen = new Date(chosenDate);
const now = new Date();
let utcDateString = dateChosen.toISOString();
if (dateChosen <= now) {
const newDate = new Date();
newDate.setSeconds(newDate.getSeconds() + 60);
utcDateString = newDate.toISOString();
}
const parameters = {
serialNumber: selectedDeviceId,
when: dateChosen <= now ? 0 : dateToUnix(utcDateString),
when: isNow ? 0 : dateToUnix(new Date(chosenDate)),
network: chosenInterface,
};
@@ -93,158 +102,218 @@ const TraceModal = ({ show, toggleModal }) => {
axiosInstance
.post(`/device/${encodeURIComponent(selectedDeviceId)}/trace`, parameters, { headers })
.then(() => {
.then((response) => {
setHadSuccess(true);
if(waitForTrace) {
setCommandUuid(response.data.UUID);
setWaitingForTrace(true);
}
})
.catch(() => {
setResponseBody(t('commands.error'));
setHadFailure(true);
})
.finally(() => {
setWaiting(false);
setBlockFields(false);
setBlockFields(false);
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
});
};
useEffect(() => {
if (selectedDeviceId !== null && show) {
const asyncGet = async () => {
const isConnected = await getDeviceConnection(selectedDeviceId);
setIsDeviceConnected(isConnected);
};
asyncGet();
}
}, [show]);
const getBody = () => {
if(waitingForTrace){
return (
<WaitingForTraceBody serialNumber={selectedDeviceId} commandUuid={commandUuid}/>
);
}
if(hadSuccess){
return(
<SuccessfulActionModalBody toggleModal={toggleModal} />
);
}
return (
<div>
<CModalBody>
<h6>{t('trace.directions')}</h6>
<CRow className={styles.spacedRow}>
<CCol>
<CButton
disabled={blockFields}
block
color="primary"
onClick={() => setUsingDuration(true)}
>
{t('common.duration')}
</CButton>
</CCol>
<CCol>
<CButton
disabled={blockFields}
block
color="primary"
onClick={() => setUsingDuration(false)}
>
{t('trace.packets')}
</CButton>
</CCol>
</CRow>
<CRow className={styles.spacedRow}>
<CCol md="4" className={styles.spacedColumn}>
{usingDuration ? 'Duration: ' : 'Packets: '}
</CCol>
<CCol xs="12" md="8">
{usingDuration ? (
<CSelect defaultValue="duration" disabled={blockFields}>
<option value="20" onClick={() => setDuration(20)}>
20s
</option>
<option value="40" onClick={() => setDuration(40)}>
40s
</option>
<option value="60" onClick={() => setDuration(60)}>
60s
</option>
<option value="120" onClick={() => setDuration(120)}>
120s
</option>
</CSelect>
) : (
<CSelect defaultValue={packets} disabled={blockFields}>
<option value="100" onClick={() => setPackets(100)}>
100
</option>
<option value="250" onClick={() => setPackets(250)}>
250
</option>
<option value="500" onClick={() => setPackets(500)}>
500
</option>
<option value="1000" onClick={() => setPackets(1000)}>
1000
</option>
</CSelect>
)}
</CCol>
</CRow>
<CRow className={styles.spacedRow}>
<CCol md="7">{t('trace.choose_network')}:</CCol>
<CCol>
<CForm>
<CFormGroup variant="checkbox" onClick={() => setChosenInterface('up')}>
<CInputRadio
defaultChecked={chosenInterface === 'up'}
id="traceRadio1"
name="radios"
value="traceOption1"
/>
<CLabel variant="checkbox" htmlFor="traceRadio1">
Up
</CLabel>
</CFormGroup>
<CFormGroup variant="checkbox" onClick={() => setChosenInterface('down')}>
<CInputRadio
defaultChecked={chosenInterface === 'down'}
id="traceRadio2"
name="radios"
value="traceOption2"
/>
<CLabel variant="checkbox" htmlFor="traceRadio2">
Down
</CLabel>
</CFormGroup>
</CForm>
</CCol>
</CRow>
<CRow className={styles.spacedRow}>
<CCol md="8">
<p className={styles.spacedText}>{t('common.execute_now')}</p>
</CCol>
<CCol>
<CSwitch
disabled={blockFields}
color="primary"
defaultChecked={isNow}
onClick={toggleNow}
labelOn={t('common.yes')}
labelOff={t('common.no')}
/>
</CCol>
</CRow>
<CRow className={styles.spacedRow} hidden={isNow}>
<CCol md="4" className={styles.spacedColumn}>
<p>{t('common.date')}:</p>
</CCol>
<CCol xs="12" md="8">
<DatePicker
selected={new Date(chosenDate)}
includeTime
value={new Date(chosenDate)}
placeholder="Select custom date"
disabled={blockFields}
onChange={(date) => setDate(date)}
min={new Date()}
/>
</CCol>
</CRow>
<CInvalidFeedback>{t('common.need_date')}</CInvalidFeedback>
<CRow className={styles.spacedRow} hidden={!isNow || !isDeviceConnected}>
<CCol md="8">
<p className={styles.spacedText}>
{t('upgrade.wait_for_upgrade')}
</p>
</CCol>
<CCol>
<CSwitch
disabled={blockFields}
color="primary"
defaultChecked={waitForTrace}
onClick={toggleWaitForTrace}
labelOn={t('common.yes')}
labelOff={t('common.no')}
/>
</CCol>
</CRow>
<div hidden={!hadSuccess && !hadFailure}>
<div>
<pre className="ignore">{responseBody} </pre>
</div>
</div>
</CModalBody>
<CModalFooter>
<LoadingButton
label="Schedule"
isLoadingLabel="Loading..."
isLoading={blockFields}
action={doAction}
variant="outline"
block={false}
disabled={blockFields}
/>
<CButton color="secondary" onClick={toggleModal}>
{t('common.cancel')}
</CButton>
</CModalFooter>
</div>
);
}
return (
<CModal show={show} onClose={toggleModal}>
<CModalHeader closeButton>
<CModalTitle>{t('trace.title')}</CModalTitle>
</CModalHeader>
{hadSuccess ? (
<SuccessfulActionModalBody toggleModal={toggleModal} />
) : (
<div>
<CModalBody>
<h6>{t('trace.directions')}</h6>
<CRow className={styles.spacedRow}>
<CCol>
<CButton
disabled={waiting}
block
color="primary"
onClick={() => setUsingDuration(true)}
>
{t('common.duration')}
</CButton>
</CCol>
<CCol>
<CButton
disabled={waiting}
block
color="primary"
onClick={() => setUsingDuration(false)}
>
{t('trace.packets')}
</CButton>
</CCol>
</CRow>
<CRow className={styles.spacedRow}>
<CCol md="4" className={styles.spacedColumn}>
{usingDuration ? 'Duration: ' : 'Packets: '}
</CCol>
<CCol xs="12" md="8">
{usingDuration ? (
<CSelect defaultValue="duration" disabled={waiting}>
<option value="20" onClick={() => setDuration(20)}>
20s
</option>
<option value="40" onClick={() => setDuration(40)}>
40s
</option>
<option value="60" onClick={() => setDuration(60)}>
60s
</option>
<option value="120" onClick={() => setDuration(120)}>
120s
</option>
</CSelect>
) : (
<CSelect defaultValue={packets} disabled={waiting}>
<option value="100" onClick={() => setPackets(100)}>
100
</option>
<option value="250" onClick={() => setPackets(250)}>
250
</option>
<option value="500" onClick={() => setPackets(500)}>
500
</option>
<option value="1000" onClick={() => setPackets(1000)}>
1000
</option>
</CSelect>
)}
</CCol>
</CRow>
<CRow className={styles.spacedRow}>
<CCol md="4" className={styles.spacedColumn}>
<p>{t('common.date')}:</p>
</CCol>
<CCol xs="12" md="8">
<DatePicker
selected={new Date(chosenDate)}
includeTime
value={new Date(chosenDate)}
placeholder="Select custom date"
disabled={waiting}
onChange={(date) => setDate(date)}
min={convertDateToUtc(new Date())}
/>
</CCol>
</CRow>
<CInvalidFeedback>{t('common.need_date')}</CInvalidFeedback>
<CRow className={styles.spacedRow}>
<CCol md="7">{t('trace.choose_network')}:</CCol>
<CCol>
<CForm>
<CFormGroup variant="checkbox" onClick={() => setChosenInterface('up')}>
<CInputRadio
defaultChecked={chosenInterface === 'up'}
id="traceRadio1"
name="radios"
value="traceOption1"
/>
<CLabel variant="checkbox" htmlFor="traceRadio1">
Up
</CLabel>
</CFormGroup>
<CFormGroup variant="checkbox" onClick={() => setChosenInterface('down')}>
<CInputRadio
defaultChecked={chosenInterface === 'down'}
id="traceRadio2"
name="radios"
value="traceOption2"
/>
<CLabel variant="checkbox" htmlFor="traceRadio2">
Down
</CLabel>
</CFormGroup>
</CForm>
</CCol>
</CRow>
<div hidden={!hadSuccess && !hadFailure}>
<div>
<pre className="ignore">{responseBody} </pre>
</div>
</div>
</CModalBody>
<CModalFooter>
<LoadingButton
label="Schedule"
isLoadingLabel="Loading..."
isLoading={waiting}
action={doAction}
variant="outline"
block={false}
disabled={waiting}
/>
<CButton color="secondary" onClick={toggleModal}>
{t('common.cancel')}
</CButton>
</CModalFooter>
</div>
)}
{getBody()}
</CModal>
);
};

View File

@@ -5,3 +5,7 @@
.spacedColumn {
margin-top: 7px;
}
.centerDiv {
text-align: center;
}

View File

@@ -1,6 +1,6 @@
.sidebarImgFull {
height: 75px;
width: 75px;
height: 100px;
width: 230px;
}
.sidebarImgMinimized {

16
src/utils/deviceHelper.js Normal file
View File

@@ -0,0 +1,16 @@
import { getToken } from 'utils/authHelper';
import axiosInstance from 'utils/axiosInstance';
export default async (deviceId) => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${getToken()}`,
},
};
return axiosInstance
.get(`/device/${encodeURIComponent(deviceId)}/status`, options)
.then((response) => response.data.connected)
.catch(() => false);
};