Merge pull request #58 from stephb9959/bug/54-improved-post-action-experience

Bug/54 improved post action experience
This commit is contained in:
Charles
2021-06-05 08:18:42 -04:00
committed by GitHub
21 changed files with 736 additions and 777 deletions

View File

@@ -2,8 +2,23 @@ import React from 'react';
import PropTypes from 'prop-types';
import { CButton, CSpinner } from '@coreui/react';
const LoadingButton = ({ isLoading, label, isLoadingLabel, action, color, variant}) => (
<CButton variant={variant} disabled={isLoading} color={color} onClick={() => action()} block>
const LoadingButton = ({
isLoading,
label,
isLoadingLabel,
action,
color,
variant,
block,
disabled,
}) => (
<CButton
variant={variant}
color={color}
onClick={() => action()}
block={block}
disabled={isLoading || disabled}
>
{isLoading ? isLoadingLabel : label}
<CSpinner hidden={!isLoading} component="span" size="sm" />
</CButton>
@@ -11,16 +26,20 @@ const LoadingButton = ({ isLoading, label, isLoadingLabel, action, color, varian
LoadingButton.propTypes = {
isLoading: PropTypes.bool.isRequired,
block: PropTypes.bool,
disabled: PropTypes.bool,
label: PropTypes.string.isRequired,
isLoadingLabel: PropTypes.string.isRequired,
action: PropTypes.func.isRequired,
color: PropTypes.string,
variant: PropTypes.string
variant: PropTypes.string,
};
LoadingButton.defaultProps = {
color: "primary",
variant: ""
}
color: 'primary',
variant: '',
block: true,
disabled: false,
};
export default LoadingButton;

View File

@@ -0,0 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import { CButton, CModalBody, CModalFooter } from '@coreui/react';
const SuccessfulActionModalBody = ({ toggleModal }) => (
<div>
<CModalBody>
<h6>Command submitted successfully</h6>
</CModalBody>
<CModalFooter>
<CButton variant="outline" color="primary" onClick={() => toggleModal()} block>
Dismiss
</CButton>
</CModalFooter>
</div>
);
SuccessfulActionModalBody.propTypes = {
toggleModal: PropTypes.func.isRequired,
};
export default SuccessfulActionModalBody;

View File

@@ -3,7 +3,7 @@ import { CFooter } from '@coreui/react';
const TheFooter = () => (
<CFooter fixed={false}>
<div>Version 0.0.11</div>
<div>Version 0.0.12</div>
<div className="mfs-auto">
<span className="mr-1">Powered by</span>
<a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer">

View File

@@ -66,7 +66,10 @@ const DeviceList = () => {
const startIndex = page * devicesPerPage;
const endIndex = parseInt(startIndex, 10) + parseInt(devicesPerPage, 10);
const serialsToGet = serialNumbers.slice(startIndex, endIndex).map(x => encodeURIComponent(x)).join(',');
const serialsToGet = serialNumbers
.slice(startIndex, endIndex)
.map((x) => encodeURIComponent(x))
.join(',');
axiosInstance
.get(`/devices?deviceWithStatus=true&select=${serialsToGet}`, {

View File

@@ -19,6 +19,8 @@ import 'react-widgets/styles.css';
import { getToken } from '../../utils/authHelper';
import axiosInstance from '../../utils/axiosInstance';
import eventBus from '../../utils/EventBus';
import LoadingButton from '../../components/LoadingButton';
import SuccessfulActionModalBody from '../../components/SuccessfulActionModalBody';
const ActionModal = ({ show, toggleModal, title, directions, action }) => {
const [hadSuccess, setHadSuccess] = useState(false);
@@ -28,17 +30,8 @@ const ActionModal = ({ show, toggleModal, title, directions, action }) => {
const [chosenDate, setChosenDate] = useState(new Date().toString());
const [doingNow, setDoingNow] = useState(false);
const [responseBody, setResponseBody] = useState('');
const [checkingIfSure, setCheckingIfSure] = useState(false);
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
const formValidation = () => {
if (chosenDate === '') {
setValidDate(false);
return false;
}
return true;
};
const setDateToLate = () => {
const date = convertDateToUtc(new Date());
if (date.getHours() >= 3) {
@@ -56,11 +49,8 @@ const ActionModal = ({ show, toggleModal, title, directions, action }) => {
}
};
const confirmingIfSure = () => {
setCheckingIfSure(true);
};
useEffect(() => {
if (show) {
setHadSuccess(false);
setHadFailure(false);
setWaiting(false);
@@ -68,11 +58,11 @@ const ActionModal = ({ show, toggleModal, title, directions, action }) => {
setChosenDate(new Date().toString());
setResponseBody('');
setValidDate(true);
setCheckingIfSure(false);
}
}, [show]);
const doAction = (isNow) => {
setDoingNow(isNow);
if (isNow !== undefined) setDoingNow(isNow);
setHadFailure(false);
setHadSuccess(false);
setWaiting(true);
@@ -103,7 +93,6 @@ const ActionModal = ({ show, toggleModal, title, directions, action }) => {
})
.finally(() => {
setDoingNow(false);
setCheckingIfSure(false);
setWaiting(false);
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
});
@@ -114,6 +103,10 @@ const ActionModal = ({ show, toggleModal, title, directions, action }) => {
<CModalHeader closeButton>
<CModalTitle>{title}</CModalTitle>
</CModalHeader>
{hadSuccess ? (
<SuccessfulActionModalBody toggleModal={toggleModal} />
) : (
<div>
<CModalBody>
<h6>{directions}</h6>
<CRow style={{ marginTop: '20px' }}>
@@ -155,28 +148,21 @@ const ActionModal = ({ show, toggleModal, title, directions, action }) => {
</div>
</CModalBody>
<CModalFooter>
<div hidden={!checkingIfSure}>Are you sure?</div>
<CButton
hidden={checkingIfSure}
<LoadingButton
label="Schedule"
isLoadingLabel="Loading..."
isLoading={waiting}
action={doAction}
variant="outline"
block={false}
disabled={waiting}
color="primary"
onClick={() => (formValidation() ? confirmingIfSure() : null)}
>
Schedule
</CButton>
<CButton
hidden={!checkingIfSure}
disabled={waiting}
color="primary"
onClick={() => (formValidation() ? doAction(false) : null)}
>
{waiting && !doingNow ? 'Loading...' : 'Yes'} {' '}
<CSpinner hidden={!waiting || doingNow} component="span" size="sm" />
</CButton>
/>
<CButton color="secondary" onClick={toggleModal}>
Cancel
</CButton>
</CModalFooter>
</div>
)}
</CModal>
);
};

View File

@@ -22,6 +22,8 @@ import 'react-widgets/styles.css';
import { getToken } from '../../utils/authHelper';
import axiosInstance from '../../utils/axiosInstance';
import eventBus from '../../utils/EventBus';
import SuccessfulActionModalBody from '../../components/SuccessfulActionModalBody';
import LoadingButton from '../../components/LoadingButton';
const BlinkModal = ({ show, toggleModal }) => {
const [hadSuccess, setHadSuccess] = useState(false);
@@ -31,7 +33,6 @@ const BlinkModal = ({ show, toggleModal }) => {
const [chosenDate, setChosenDate] = useState(new Date().toString());
const [chosenPattern, setPattern] = useState('on');
const [responseBody, setResponseBody] = useState('');
const [checkingIfSure, setCheckingIfSure] = useState(false);
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
const setDateToLate = () => {
@@ -51,23 +52,20 @@ const BlinkModal = ({ show, toggleModal }) => {
}
};
const confirmingIfSure = () => {
setCheckingIfSure(true);
};
useEffect(() => {
setHadSuccess(false);
setHadFailure(false);
if (show) {
setWaiting(false);
setChosenDate(new Date().toString());
setResponseBody('');
setPattern('on');
setCheckingIfSure(false);
setDoingNow(false);
setHadSuccess(false);
setHadFailure(false);
}
}, [show]);
const doAction = (isNow) => {
setDoingNow(isNow);
if (isNow !== undefined) setDoingNow(isNow);
setHadFailure(false);
setHadSuccess(false);
setWaiting(true);
@@ -100,7 +98,6 @@ const BlinkModal = ({ show, toggleModal }) => {
})
.finally(() => {
setDoingNow(false);
setCheckingIfSure(false);
setWaiting(false);
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
});
@@ -111,6 +108,10 @@ const BlinkModal = ({ show, toggleModal }) => {
<CModalHeader closeButton>
<CModalTitle>LEDs of Device</CModalTitle>
</CModalHeader>
{hadSuccess ? (
<SuccessfulActionModalBody toggleModal={toggleModal} />
) : (
<div>
<CModalBody>
<h6>When would you like make the LEDs of this device blink?</h6>
<CRow style={{ marginTop: '20px' }}>
@@ -189,28 +190,21 @@ const BlinkModal = ({ show, toggleModal }) => {
</div>
</CModalBody>
<CModalFooter>
<div hidden={!checkingIfSure}>Are you sure?</div>
<CButton
<LoadingButton
label="Schedule"
isLoadingLabel="Loading..."
isLoading={waiting && !doingNow}
action={doAction}
variant="outline"
block={false}
disabled={waiting}
hidden={checkingIfSure}
color="primary"
onClick={() => confirmingIfSure()}
>
Schedule
</CButton>
<CButton
hidden={!checkingIfSure}
disabled={waiting}
color="primary"
onClick={() => doAction(false)}
>
{waiting && !doingNow ? 'Loading...' : 'Yes'} {' '}
<CSpinner hidden={!waiting || doingNow} component="span" size="sm" />
</CButton>
/>
<CButton color="secondary" onClick={toggleModal}>
Cancel
</CButton>
</CModalFooter>
</div>
)}
</CModal>
);
};

View File

@@ -10,18 +10,19 @@ import {
CRow,
CForm,
CTextarea,
CInvalidFeedback
} from '@coreui/react';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import 'react-widgets/styles.css';
import { getToken } from '../../utils/authHelper';
import { checkIfJson } from '../../utils/helper';
import axiosInstance from '../../utils/axiosInstance';
import eventBus from '../../utils/EventBus';
CInvalidFeedback,
} from '@coreui/react';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import 'react-widgets/styles.css';
import { getToken } from '../../utils/authHelper';
import { checkIfJson } from '../../utils/helper';
import axiosInstance from '../../utils/axiosInstance';
import eventBus from '../../utils/EventBus';
import SuccessfulActionModalBody from '../../components/SuccessfulActionModalBody';
const ConfigureModal = ({ show, toggleModal }) => {
const ConfigureModal = ({ show, toggleModal }) => {
const [hadSuccess, setHadSuccess] = useState(false);
const [hadFailure, setHadFailure] = useState(false);
const [doingNow, setDoingNow] = useState(false);
@@ -33,10 +34,9 @@ import {
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
const confirmingIfSure = () => {
if (checkIfJson(newConfig)){
if (checkIfJson(newConfig)) {
setCheckingIfSure(true);
}
else {
} else {
setErrorJson(true);
}
};
@@ -99,6 +99,10 @@ import {
<CModalHeader closeButton>
<CModalTitle>Configure Device</CModalTitle>
</CModalHeader>
{hadSuccess ? (
<SuccessfulActionModalBody toggleModal={toggleModal} />
) : (
<div>
<CModalBody>
<h6>Enter new device configuration: </h6>
<CRow style={{ marginTop: '20px' }}>
@@ -148,14 +152,15 @@ import {
Cancel
</CButton>
</CModalFooter>
</div>
)}
</CModal>
);
};
};
ConfigureModal.propTypes = {
ConfigureModal.propTypes = {
show: PropTypes.bool.isRequired,
toggleModal: PropTypes.func.isRequired,
};
export default ConfigureModal;
};
export default ConfigureModal;

View File

@@ -58,7 +58,7 @@ const DeviceCommands = ({ selectedDeviceId }) => {
const showMoreCommands = () => {
setCommandLimit(commandLimit + 50);
}
};
const modifyStart = (value) => {
setStart(value);
@@ -86,17 +86,17 @@ const DeviceCommands = ({ selectedDeviceId }) => {
Authorization: `Bearer ${getToken()}`,
},
params: {
limit: commandLimit
limit: commandLimit,
},
};
let extraParams = '&newest=true';
if(start !=='' && end !==''){
if (start !== '' && end !== '') {
const utcStart = new Date(start).toISOString();
const utcEnd = new Date(end).toISOString();
options.params.startDate = dateToUnix(utcStart);
options.params.endDate = dateToUnix(utcEnd);
extraParams='';
extraParams = '';
}
axiosInstance
@@ -204,8 +204,7 @@ const DeviceCommands = ({ selectedDeviceId }) => {
useEffect(() => {
if (selectedDeviceId && start !== '' && end !== '') {
getCommands();
}
else if(selectedDeviceId && start === '' && end === ''){
} else if (selectedDeviceId && start === '' && end === '') {
getCommands();
}
}, [selectedDeviceId, start, end]);
@@ -238,8 +237,7 @@ const DeviceCommands = ({ selectedDeviceId }) => {
useEffect(() => {
if (commands.length === 0 || (commands.length > 0 && commands.length < commandLimit)) {
setShowLoadingMore(false);
}
else {
} else {
setShowLoadingMore(true);
}
}, [commands]);
@@ -270,17 +268,11 @@ const DeviceCommands = ({ selectedDeviceId }) => {
<CRow style={{ marginBottom: '10px' }}>
<CCol>
From:
<DatePicker
includeTime
onChange={(date) => modifyStart(date)}
/>
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
</CCol>
<CCol>
To:
<DatePicker
includeTime
onChange={(date) => modifyEnd(date)}
/>
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
</CCol>
</CRow>
<CCard>
@@ -384,8 +376,8 @@ const DeviceCommands = ({ selectedDeviceId }) => {
),
}}
/>
<CRow style={{marginBottom: '1%', marginRight: '1%'}}>
{showLoadingMore &&
<CRow style={{ marginBottom: '1%', marginRight: '1%' }}>
{showLoadingMore && (
<LoadingButton
label="View More"
isLoadingLabel="Loading More..."
@@ -393,7 +385,7 @@ const DeviceCommands = ({ selectedDeviceId }) => {
action={showMoreCommands}
variant="outline"
/>
}
)}
</CRow>
</div>
</CCard>

View File

@@ -144,12 +144,12 @@ const DeviceConfiguration = ({ selectedDeviceId }) => {
</CFormGroup>
</CCollapse>
<CCardFooter>
<CButton
show={collapse ? 'true' : 'false'}
onClick={toggle}
block
>
<CIcon style={{color: 'black'}} name={collapse ? 'cilChevronTop' : 'cilChevronBottom'} size="lg" />
<CButton show={collapse ? 'true' : 'false'} onClick={toggle} block>
<CIcon
style={{ color: 'black' }}
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
size="lg"
/>
</CButton>
</CCardFooter>
</CForm>

View File

@@ -47,7 +47,7 @@ const DeviceHealth = ({ selectedDeviceId }) => {
const showMoreLogs = () => {
setLogLimit(logLimit + 50);
}
};
const getDeviceHealth = () => {
if (loading) return;
@@ -60,17 +60,17 @@ const DeviceHealth = ({ selectedDeviceId }) => {
Authorization: `Bearer ${getToken()}`,
},
params: {
limit: logLimit
limit: logLimit,
},
};
let extraParams = '?newest=true';
if(start !=='' && end !==''){
if (start !== '' && end !== '') {
const utcStart = new Date(start).toISOString();
const utcEnd = new Date(end).toISOString();
options.params.startDate = dateToUnix(utcStart);
options.params.endDate = dateToUnix(utcEnd);
extraParams='';
extraParams = '';
}
axiosInstance
@@ -137,8 +137,7 @@ const DeviceHealth = ({ selectedDeviceId }) => {
useEffect(() => {
if (healthChecks.length === 0 || (healthChecks.length > 0 && healthChecks.length < logLimit)) {
setShowLoadingMore(false);
}
else {
} else {
setShowLoadingMore(true);
}
}, [healthChecks]);
@@ -146,8 +145,7 @@ const DeviceHealth = ({ selectedDeviceId }) => {
useEffect(() => {
if (selectedDeviceId && start !== '' && end !== '') {
getDeviceHealth();
}
else if(selectedDeviceId && start === '' && end === ''){
} else if (selectedDeviceId && start === '' && end === '') {
getDeviceHealth();
}
}, [start, end, selectedDeviceId]);
@@ -176,17 +174,11 @@ const DeviceHealth = ({ selectedDeviceId }) => {
<CRow style={{ marginBottom: '10px' }}>
<CCol>
From:
<DatePicker
includeTime
onChange={(date) => modifyStart(date)}
/>
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
</CCol>
<CCol>
To:
<DatePicker
includeTime
onChange={(date) => modifyEnd(date)}
/>
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
</CCol>
</CRow>
<CCard>
@@ -231,8 +223,8 @@ const DeviceHealth = ({ selectedDeviceId }) => {
),
}}
/>
<CRow style={{marginBottom: '1%', marginRight: '1%'}}>
{showLoadingMore &&
<CRow style={{ marginBottom: '1%', marginRight: '1%' }}>
{showLoadingMore && (
<LoadingButton
label="View More"
isLoadingLabel="Loading More..."
@@ -240,7 +232,7 @@ const DeviceHealth = ({ selectedDeviceId }) => {
action={showMoreLogs}
variant="outline"
/>
}
)}
</CRow>
</div>
</CCard>

View File

@@ -44,7 +44,7 @@ const DeviceLogs = ({ selectedDeviceId }) => {
const showMoreLogs = () => {
setLogLimit(logLimit + 50);
}
};
const getLogs = () => {
if (loading) return;
@@ -57,17 +57,17 @@ const DeviceLogs = ({ selectedDeviceId }) => {
Authorization: `Bearer ${getToken()}`,
},
params: {
limit: logLimit
limit: logLimit,
},
};
let extraParams = '?newest=true';
if(start !=='' && end !==''){
if (start !== '' && end !== '') {
const utcStart = new Date(start).toISOString();
const utcEnd = new Date(end).toISOString();
options.params.startDate = dateToUnix(utcStart);
options.params.endDate = dateToUnix(utcEnd);
extraParams='';
extraParams = '';
}
axiosInstance
@@ -133,8 +133,7 @@ const DeviceLogs = ({ selectedDeviceId }) => {
useEffect(() => {
if (logs.length === 0 || (logs.length > 0 && logs.length < logLimit)) {
setShowLoadingMore(false);
}
else {
} else {
setShowLoadingMore(true);
}
}, [logs]);
@@ -142,8 +141,7 @@ const DeviceLogs = ({ selectedDeviceId }) => {
useEffect(() => {
if (selectedDeviceId && start !== '' && end !== '') {
getLogs();
}
else if(selectedDeviceId && start === '' && end === ''){
} else if (selectedDeviceId && start === '' && end === '') {
getLogs();
}
}, [start, end, selectedDeviceId]);
@@ -159,17 +157,11 @@ const DeviceLogs = ({ selectedDeviceId }) => {
<CRow style={{ marginBottom: '10px' }}>
<CCol>
From:
<DatePicker
includeTime
onChange={(date) => modifyStart(date)}
/>
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
</CCol>
<CCol>
To:
<DatePicker
includeTime
onChange={(date) => modifyEnd(date)}
/>
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
</CCol>
</CRow>
<CCard>
@@ -208,8 +200,8 @@ const DeviceLogs = ({ selectedDeviceId }) => {
),
}}
/>
<CRow style={{marginBottom: '1%', marginRight: '1%'}}>
{showLoadingMore &&
<CRow style={{ marginBottom: '1%', marginRight: '1%' }}>
{showLoadingMore && (
<LoadingButton
label="View More"
isLoadingLabel="Loading More..."
@@ -217,7 +209,7 @@ const DeviceLogs = ({ selectedDeviceId }) => {
action={showMoreLogs}
variant="outline"
/>
}
)}
</CRow>
</div>
</CCard>

View File

@@ -16,7 +16,8 @@ const DevicePage = () => {
const previouslySelectedDeviceId = useSelector((state) => state.selectedDeviceId);
useEffect(() => {
if (deviceId && deviceId !== previouslySelectedDeviceId) dispatch({ type: 'set', selectedDeviceId: deviceId });
if (deviceId && deviceId !== previouslySelectedDeviceId)
dispatch({ type: 'set', selectedDeviceId: deviceId });
}, [deviceId]);
return (

View File

@@ -9,9 +9,9 @@ const DeviceLifetimeStatistics = ({ selectedDeviceId }) => {
const [loading, setLoading] = useState(false);
const displayInformation = (data) => {
const sortedData = data.sort((a,b) => {
if(a.recorded > b.recorded) return 1;
if(b.recorded > a.recorded) return -1;
const sortedData = data.sort((a, b) => {
if (a.recorded > b.recorded) return 1;
if (b.recorded > a.recorded) return -1;
return 0;
});
const interfaces = [
@@ -19,28 +19,28 @@ const DeviceLifetimeStatistics = ({ selectedDeviceId }) => {
label: 'wan',
backgroundColor: 'rgb(228,102,81,0.9)',
data: [],
fill: false
fill: false,
},
{
label: 'lan',
backgroundColor: 'rgb(0,216,255,0.9)',
data: [],
fill: false
}
fill: false,
},
];
const interfaceIndexes = {
'wan': 0,
'lan': 1
wan: 0,
lan: 1,
};
for (const log of sortedData){
for (const inter of log.data.interfaces){
for (const log of sortedData) {
for (const inter of log.data.interfaces) {
interfaces[interfaceIndexes[inter.name]].data.push(inter.counters.tx_bytes);
}
}
setDataset(interfaces);
}
};
const getStatistics = () => {
if (loading) return;
@@ -52,9 +52,9 @@ const DeviceLifetimeStatistics = ({ selectedDeviceId }) => {
Authorization: `Bearer ${getToken()}`,
},
params: {
serialNumber: "24f5a207a130",
lifetime: true
}
serialNumber: '24f5a207a130',
lifetime: true,
},
};
axiosInstance
@@ -76,12 +76,10 @@ const DeviceLifetimeStatistics = ({ selectedDeviceId }) => {
return (
<div>
<CChartLine
datasets={dataset}
/>
<CChartLine datasets={dataset} />
</div>
);
}
};
DeviceLifetimeStatistics.propTypes = {
selectedDeviceId: PropTypes.string.isRequired,

View File

@@ -1,13 +1,6 @@
import React, {useState} from 'react';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import {
CCard,
CCardHeader,
CCardBody,
CPopover,
CRow,
CCol
} from '@coreui/react';
import { CCard, CCardHeader, CCardBody, CPopover, CRow, CCol } from '@coreui/react';
import { cilSync } from '@coreui/icons';
import CIcon from '@coreui/icons-react';
import StatisticsChartList from './StatisticsChartList';
@@ -17,30 +10,28 @@ const DeviceStatisticsCard = ({ selectedDeviceId }) => {
const refresh = () => {
setLastRefresh(new Date().toString());
}
};
return (
<CCard>
<CCardHeader>
<CRow>
<CCol>
Statistics
</CCol>
<CCol style={{textAlign: 'right'}}>
<CCol>Statistics</CCol>
<CCol style={{ textAlign: 'right' }}>
<CPopover content="Refresh">
<CIcon
onClick={() => refresh()}
name="cil-sync"
content={cilSync}
size="lg"
color='primary'
color="primary"
/>
</CPopover>
</CCol>
</CRow>
</CCardHeader>
<CCardBody style={{padding: '5%'}}>
<StatisticsChartList selectedDeviceId={selectedDeviceId} lastRefresh={lastRefresh}/>
<CCardBody style={{ padding: '5%' }}>
<StatisticsChartList selectedDeviceId={selectedDeviceId} lastRefresh={lastRefresh} />
</CCardBody>
</CCard>
);

View File

@@ -1,15 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import Chart from 'react-apexcharts'
import Chart from 'react-apexcharts';
const DeviceStatisticsChart = ({ data, options }) => (
<div style={{height: '360px'}}>
<Chart
series={data}
options={options}
type='line'
height='100%'
/>
<div style={{ height: '360px' }}>
<Chart series={data} options={options} type="line" height="100%" />
</div>
);

View File

@@ -12,48 +12,52 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
const [statOptions, setStatOptions] = useState({});
const transformIntoDataset = (data) => {
const sortedData = data.sort((a,b) => {
if(a.recorded > b.recorded) return 1;
if(b.recorded > a.recorded) return -1;
const sortedData = data.sort((a, b) => {
if (a.recorded > b.recorded) return 1;
if (b.recorded > a.recorded) return -1;
return 0;
});
// This dictionary will have a key that is the interface name and a value of it's index in the final array
const interfaceTypes = {}
const interfaceTypes = {};
const interfaceList = [];
const categories = [];
let i = 0;
// Just building the array for all the interfaces
for (const log of sortedData){
for (const log of sortedData) {
categories.push(unixToTime(log.recorded));
for (const logInterface of log.data.interfaces){
if(interfaceTypes[logInterface.name] === undefined) {
for (const logInterface of log.data.interfaces) {
if (interfaceTypes[logInterface.name] === undefined) {
interfaceTypes[logInterface.name] = i;
interfaceList.push([{
interfaceList.push([
{
titleName: logInterface.name,
name: 'Tx',
backgroundColor: 'rgb(228,102,81,0.9)',
data: [],
fill: false
fill: false,
},
{
titleName: logInterface.name,
name: 'Rx',
backgroundColor: 'rgb(0,216,255,0.9)',
data: [],
fill: false
}]);
fill: false,
},
]);
i += 1;
}
}
}
// Looping through all the data
for (const log of sortedData){
for (const log of sortedData) {
// 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));
for (const inter of log.data.interfaces) {
interfaceList[interfaceTypes[inter.name]][0].data.push(
Math.floor(inter.counters.tx_bytes / 1024),
);
interfaceList[interfaceTypes[inter.name]][1].data.push(Math.floor(inter.counters.rx_bytes));
}
}
@@ -61,43 +65,42 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
const options = {
chart: {
id: 'chart',
group: 'txrx'
group: 'txrx',
},
xaxis: {
title: {
text: 'Time',
style: {
fontSize: '15px'
fontSize: '15px',
},
},
categories,
tickAmount:20
tickAmount: 20,
},
yaxis: {
labels: {
minWidth: 40
minWidth: 40,
},
title: {
text: 'Data (KB)',
style: {
fontSize: '15px'
fontSize: '15px',
},
},
},
legend: {
position: 'top',
horizontalAlign: 'right',
float: true
float: true,
},
};
setStatOptions(options);
setStats(interfaceList);
}
};
const getStatistics = () => {
if (!loading){
if (!loading) {
setLoading(true);
const options = {
@@ -106,8 +109,8 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
Authorization: `Bearer ${getToken()}`,
},
params: {
serialNumber: "24f5a207a130"
}
serialNumber: '24f5a207a130',
},
};
axiosInstance
@@ -129,17 +132,14 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
}, [selectedDeviceId]);
useEffect(() => {
if (lastRefresh !== '' && selectedDeviceId){
if (lastRefresh !== '' && selectedDeviceId) {
getStatistics();
}
}, [lastRefresh])
}, [lastRefresh]);
return (
<div>
{
deviceStats.map(
(data) =>
{deviceStats.map((data) => (
<div key={createUuid()}>
<DeviceStatisticsChart
key={createUuid()}
@@ -150,16 +150,16 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
text: capitalizeFirstLetter(data[0].titleName),
align: 'left',
style: {
fontSize: '25px'
fontSize: '25px',
},
}
}} />
},
}}
/>
</div>
)
}
))}
</div>
);
}
};
StatisticsChartList.propTypes = {
lastRefresh: PropTypes.string,
@@ -167,7 +167,7 @@ StatisticsChartList.propTypes = {
};
StatisticsChartList.defaultProps = {
lastRefresh: ''
}
lastRefresh: '',
};
export default StatisticsChartList;

View File

@@ -5,8 +5,6 @@ import {
CModalTitle,
CModalBody,
CModalFooter,
CSpinner,
CBadge,
CCol,
CRow,
CInvalidFeedback,
@@ -21,6 +19,7 @@ import 'react-widgets/styles.css';
import { getToken } from '../../utils/authHelper';
import axiosInstance from '../../utils/axiosInstance';
import eventBus from '../../utils/EventBus';
import LoadingButton from '../../components/LoadingButton';
const TraceModal = ({ show, toggleModal }) => {
const [hadSuccess, setHadSuccess] = useState(false);
@@ -31,7 +30,6 @@ const TraceModal = ({ show, toggleModal }) => {
const [packets, setPackets] = useState(100);
const [chosenDate, setChosenDate] = useState(new Date().toString());
const [responseBody, setResponseBody] = useState('');
const [checkingIfSure, setCheckingIfSure] = useState(false);
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
const setDate = (date) => {
@@ -40,17 +38,12 @@ const TraceModal = ({ show, toggleModal }) => {
}
};
const confirmingIfSure = () => {
setCheckingIfSure(true);
};
useEffect(() => {
setHadSuccess(false);
setHadFailure(false);
setWaiting(false);
setChosenDate(new Date().toString());
setResponseBody('');
setCheckingIfSure(false);
setDuration(20);
setPackets(100);
}, [show]);
@@ -99,7 +92,6 @@ const TraceModal = ({ show, toggleModal }) => {
setHadFailure(true);
})
.finally(() => {
setCheckingIfSure(false);
setWaiting(false);
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
});
@@ -199,25 +191,15 @@ const TraceModal = ({ show, toggleModal }) => {
</div>
</CModalBody>
<CModalFooter>
<div hidden={!checkingIfSure}>Are you sure?</div>
<CButton hidden={checkingIfSure} color="primary" onClick={() => confirmingIfSure()}>
Schedule
</CButton>
<CButton
hidden={!checkingIfSure}
<LoadingButton
label="Schedule"
isLoadingLabel="Loading..."
isLoading={waiting}
action={doAction}
variant="outline"
block={false}
disabled={waiting}
color="primary"
onClick={() => doAction()}
>
{waiting ? 'Loading...' : 'Yes'} {' '}
<CSpinner hidden={!waiting} component="span" size="sm" />
<CBadge hidden={waiting || !hadSuccess} color="success" shape="pill">
Success
</CBadge>
<CBadge hidden={waiting || !hadFailure} color="danger" shape="pill">
Request Failed
</CBadge>
</CButton>
/>
<CButton color="secondary" onClick={toggleModal}>
Cancel
</CButton>

View File

@@ -5,7 +5,6 @@ import {
CModalTitle,
CModalBody,
CModalFooter,
CSpinner,
CRow,
CForm,
CSwitch,
@@ -18,6 +17,7 @@ import 'react-widgets/styles.css';
import { getToken } from '../../utils/authHelper';
import axiosInstance from '../../utils/axiosInstance';
import eventBus from '../../utils/EventBus';
import LoadingButton from '../../components/LoadingButton';
const WifiScanModal = ({ show, toggleModal }) => {
const [hadSuccess, setHadSuccess] = useState(false);
@@ -25,23 +25,17 @@ const WifiScanModal = ({ show, toggleModal }) => {
const [waiting, setWaiting] = useState(false);
const [choseVerbose, setVerbose] = useState(true);
const [channelList, setChannelList] = useState([]);
const [checkingIfSure, setCheckingIfSure] = useState(false);
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
const toggleVerbose = () => {
setVerbose(!choseVerbose);
};
const confirmingIfSure = () => {
setCheckingIfSure(true);
};
useEffect(() => {
setHadSuccess(false);
setHadFailure(false);
setWaiting(false);
setChannelList([]);
setCheckingIfSure(false);
setVerbose(true);
}, [show]);
@@ -108,7 +102,6 @@ const WifiScanModal = ({ show, toggleModal }) => {
setHadFailure(true);
})
.finally(() => {
setCheckingIfSure(false);
setWaiting(false);
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
});
@@ -138,19 +131,15 @@ const WifiScanModal = ({ show, toggleModal }) => {
</div>
</CModalBody>
<CModalFooter>
<div hidden={!checkingIfSure}>Are you sure?</div>
<CButton hidden={checkingIfSure} color="primary" onClick={() => confirmingIfSure()}>
{hadSuccess || hadFailure ? 'Re-Scan' : 'Scan'}
</CButton>
<CButton
hidden={!checkingIfSure}
<LoadingButton
label="Schedule"
isLoadingLabel="Loading..."
isLoading={waiting}
action={doAction}
variant="outline"
block={false}
disabled={waiting}
color="primary"
onClick={() => doAction()}
>
{waiting ? 'Loading...' : 'Yes'} {' '}
<CSpinner hidden={!waiting} component="span" size="sm" />
</CButton>
/>
<CButton color="secondary" onClick={toggleModal}>
Cancel
</CButton>

View File

@@ -38,24 +38,24 @@ const Login = () => {
const formValidation = () => {
setHadError(false);
let isSuccesful = true;
let isSuccessful = true;
if (userId.trim() === '') {
setEmptyUsername(true);
isSuccesful = false;
isSuccessful = false;
}
if (password.trim() === '') {
setEmptyPassword(true);
isSuccesful = false;
isSuccessful = false;
}
if (gatewayUrl.trim() === '') {
setEmptyGateway(true);
isSuccesful = false;
isSuccessful = false;
}
return isSuccesful;
return isSuccessful;
};
const SignIn = (credentials) => {

View File

@@ -60,7 +60,7 @@ export const unixToTime = (dateString) => {
return `${prettyNumber(date.getHours())}:${prettyNumber(date.getMinutes())}:${prettyNumber(
date.getSeconds(),
)}`;
}
};
export const dateToUnix = (date) => Math.floor(new Date(date).getTime() / 1000);
@@ -69,10 +69,8 @@ export const capitalizeFirstLetter = (string) => string.charAt(0).toUpperCase()
export const checkIfJson = (string) => {
try {
JSON.parse(string);
}
catch (e) {
return false
} catch (e) {
return false;
}
return true;
}
};