mirror of
https://github.com/optim-enterprises-bv/OptimCloud-gw-ui.git
synced 2025-10-28 17:02:21 +00:00
Added eslint and prettier to project
This commit is contained in:
1
.eslintignore
Normal file
1
.eslintignore
Normal file
@@ -0,0 +1 @@
|
||||
/src/assets
|
||||
@@ -11,10 +11,12 @@
|
||||
"jest": true
|
||||
},
|
||||
"rules": {
|
||||
"max-len": ["error", {"code": 100}],
|
||||
"max-len": ["error", {"code": 150}],
|
||||
"prefer-promise-reject-errors": ["off"],
|
||||
"react/jsx-filename-extension": ["off"],
|
||||
"react/prop-types": ["warn"],
|
||||
"no-return-assign": ["off"]
|
||||
"no-return-assign": ["off"],
|
||||
"react/jsx-props-no-spreading": ["off"],
|
||||
"react/destructuring-assignment": ["off"]
|
||||
}
|
||||
}
|
||||
21566
package-lock.json
generated
21566
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,7 @@
|
||||
"react-select": "^4.3.0",
|
||||
"react-widgets": "^5.1.1",
|
||||
"reactstrap": "^8.9.0",
|
||||
"redux": "^4.1.0",
|
||||
"sass": "^1.32.8",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
|
||||
44
src/App.js
44
src/App.js
@@ -5,33 +5,37 @@ import { useSelector, useDispatch } from 'react-redux';
|
||||
|
||||
const loading = (
|
||||
<div className="pt-3 text-center">
|
||||
<div className="sk-spinner sk-spinner-pulse"></div>
|
||||
<div className="sk-spinner sk-spinner-pulse" />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
const TheLayout = React.lazy(() => import('./containers/TheLayout'));
|
||||
const Login = React.lazy(() => import('./views/pages/Login'));
|
||||
|
||||
const App = () => {
|
||||
const isLoggedIn = useSelector(state => state.connected);
|
||||
const dispatch = useDispatch();
|
||||
const isLoggedIn = useSelector((state) => state.connected);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
const token = sessionStorage.getItem('access_token');
|
||||
if (token !== undefined && token !== null) {
|
||||
dispatch({type: 'set', connected: true});
|
||||
}
|
||||
}, [dispatch]);
|
||||
useEffect(() => {
|
||||
const token = sessionStorage.getItem('access_token');
|
||||
if (token !== undefined && token !== null) {
|
||||
dispatch({ type: 'set', connected: true });
|
||||
}
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<HashRouter>
|
||||
<React.Suspense fallback={loading}>
|
||||
<Switch>
|
||||
<Route path="/" name="Devices" render={props => isLoggedIn ? <TheLayout {...props}/> : <Login {...props}/>} />
|
||||
</Switch>
|
||||
</React.Suspense>
|
||||
</HashRouter>
|
||||
);
|
||||
return (
|
||||
<HashRouter>
|
||||
<React.Suspense fallback={loading}>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/"
|
||||
name="Devices"
|
||||
render={(props) => (isLoggedIn ? <TheLayout {...props} /> : <Login {...props} />)}
|
||||
/>
|
||||
</Switch>
|
||||
</React.Suspense>
|
||||
</HashRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
export default App;
|
||||
|
||||
@@ -8,7 +8,7 @@ test('renders learn react link', () => {
|
||||
const { getByText } = render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(getByText(/learn/i)).toBeInTheDocument();
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { sygnet } from './sygnet'
|
||||
import { logo } from './logo'
|
||||
import { logoNegative } from './logo-negative'
|
||||
|
||||
import {
|
||||
cibSkype,
|
||||
cibFacebook,
|
||||
@@ -27,16 +23,12 @@ import {
|
||||
cibPaypal,
|
||||
cibGooglePay,
|
||||
cibCcAmex,
|
||||
} from '@coreui/icons'
|
||||
import {
|
||||
cifUs,
|
||||
cifBr,
|
||||
cifIn,
|
||||
cifFr,
|
||||
cifEs,
|
||||
cifPl
|
||||
} from '@coreui/icons'
|
||||
import {
|
||||
cifPl,
|
||||
cilAlignCenter,
|
||||
cilAlignLeft,
|
||||
cilAlignRight,
|
||||
@@ -128,14 +120,16 @@ import {
|
||||
cilUserUnfollow,
|
||||
cilX,
|
||||
cilXCircle,
|
||||
cilWarning
|
||||
} from '@coreui/icons'
|
||||
cilWarning,
|
||||
} from '@coreui/icons';
|
||||
import { sygnet } from './sygnet';
|
||||
import { logo } from './logo';
|
||||
import { logoNegative } from './logo-negative';
|
||||
|
||||
export const icons = Object.assign({}, {
|
||||
export const icons = {
|
||||
sygnet,
|
||||
logo,
|
||||
logoNegative
|
||||
}, {
|
||||
logoNegative,
|
||||
cilAlignCenter,
|
||||
cilAlignLeft,
|
||||
cilAlignRight,
|
||||
@@ -227,15 +221,13 @@ export const icons = Object.assign({}, {
|
||||
cilUserUnfollow,
|
||||
cilX,
|
||||
cilXCircle,
|
||||
cilWarning
|
||||
}, {
|
||||
cilWarning,
|
||||
cifUs,
|
||||
cifBr,
|
||||
cifIn,
|
||||
cifFr,
|
||||
cifEs,
|
||||
cifPl
|
||||
}, {
|
||||
cifPl,
|
||||
cibSkype,
|
||||
cibFacebook,
|
||||
cibTwitter,
|
||||
@@ -259,5 +251,5 @@ export const icons = Object.assign({}, {
|
||||
cibStripe,
|
||||
cibPaypal,
|
||||
cibGooglePay,
|
||||
cibCcAmex
|
||||
})
|
||||
cibCcAmex,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export const logoNegative = ['608 134', `
|
||||
export const logoNegative = [
|
||||
'608 134',
|
||||
`
|
||||
<title>coreui react pro logo</title>
|
||||
<g>
|
||||
<g style="fill:#80d0ff;">
|
||||
@@ -27,4 +29,5 @@ export const logoNegative = ['608 134', `
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
`]
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export const logo = ['608 134', `
|
||||
export const logo = [
|
||||
'608 134',
|
||||
`
|
||||
<title>coreui react pro</title>
|
||||
<g>
|
||||
<g style="fill:#00a1ff">
|
||||
@@ -26,4 +28,5 @@ export const logo = ['608 134', `
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
`]
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export const sygnet = ['160 160', `
|
||||
export const sygnet = [
|
||||
'160 160',
|
||||
`
|
||||
<title>coreui logo</title>
|
||||
<g>
|
||||
<g style="fill:#fff;">
|
||||
@@ -6,4 +8,5 @@ export const sygnet = ['160 160', `
|
||||
<path d="M103.0216,93.0379h-2.866a4,4,0,0,0-1.9246.4935L80.95,103.0167,61,91.4981V68.5206L80.95,57.002l17.2894,9.455a4,4,0,0,0,1.9192.4905h2.8632a2,2,0,0,0,2-2V62.2357a2,2,0,0,0-1.04-1.7547L84.793,49.9854a8.0391,8.0391,0,0,0-7.8428.09L57,61.5929A8.0243,8.0243,0,0,0,53,68.5216v22.976a8,8,0,0,0,4,6.9283l19.95,11.5185a8.0422,8.0422,0,0,0,7.8433.0879l19.19-10.5311a2,2,0,0,0,1.0378-1.7534v-2.71A2,2,0,0,0,103.0216,93.0379Z"/>
|
||||
</g>
|
||||
</g>
|
||||
`]
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -1,127 +1,70 @@
|
||||
import React, { useState } from 'react'
|
||||
import {
|
||||
CButton,
|
||||
CCard,
|
||||
CCardHeader,
|
||||
CCardBody,
|
||||
CRow,
|
||||
CCol
|
||||
} from '@coreui/react'
|
||||
import React, { useState } from 'react';
|
||||
import { CButton, CCard, CCardHeader, CCardBody, CRow, CCol } from '@coreui/react';
|
||||
import ActionModalWidget from '../widgets/ActionModalWidget';
|
||||
import FirmwareUpgradeModal from './FirmwareUpgradeModal';
|
||||
|
||||
const DeviceActions = () => {
|
||||
const [showRebootModal, setShowRebootModal] = useState(false);
|
||||
const [showBlinkModal, setShowBlinkModal] = useState(false);
|
||||
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
||||
const [firmwareUri, setFirmwareUri] = useState('');
|
||||
const [validField, setValidField] = useState(true);
|
||||
|
||||
const toggleRebootModal = (e) => {
|
||||
setShowRebootModal(!showRebootModal);
|
||||
}
|
||||
const [showRebootModal, setShowRebootModal] = useState(false);
|
||||
const [showBlinkModal, setShowBlinkModal] = useState(false);
|
||||
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
||||
|
||||
const toggleBlinkModal = (e) => {
|
||||
setShowBlinkModal(!showBlinkModal);
|
||||
}
|
||||
const toggleRebootModal = () => {
|
||||
setShowRebootModal(!showRebootModal);
|
||||
};
|
||||
|
||||
const toggleUpgradeModal = (e) => {
|
||||
setShowUpgradeModal(!showUpgradeModal);
|
||||
}
|
||||
const toggleBlinkModal = () => {
|
||||
setShowBlinkModal(!showBlinkModal);
|
||||
};
|
||||
|
||||
const formChange = (fieldValue) => {
|
||||
if(!validField){
|
||||
setValidField(true);
|
||||
}
|
||||
setFirmwareUri(fieldValue);
|
||||
}
|
||||
const toggleUpgradeModal = () => {
|
||||
setShowUpgradeModal(!showUpgradeModal);
|
||||
};
|
||||
|
||||
const formValidation = () => {
|
||||
if (firmwareUri.trim() === ''){
|
||||
setValidField(false);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>Device Actions</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow>
|
||||
<CCol>
|
||||
<CButton block onClick={toggleRebootModal} color="primary">
|
||||
Reboot
|
||||
</CButton>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CButton block onClick={toggleBlinkModal} color="primary">
|
||||
Blink
|
||||
</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow style={{ marginTop: '10px' }}>
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={toggleUpgradeModal}>
|
||||
Firmware Upgrade
|
||||
</CButton>
|
||||
</CCol>
|
||||
<CCol />
|
||||
</CRow>
|
||||
</CCardBody>
|
||||
<ActionModalWidget
|
||||
show={showRebootModal}
|
||||
toggleModal={toggleRebootModal}
|
||||
title="Reboot Device"
|
||||
directions="When would you like to reboot this device?"
|
||||
actionLabel="reboot"
|
||||
action="reboot"
|
||||
/>
|
||||
<ActionModalWidget
|
||||
show={showBlinkModal}
|
||||
toggleModal={toggleBlinkModal}
|
||||
title="Blink LEDs of Device"
|
||||
directions="When would you like make the LEDs of this device blink?"
|
||||
actionLabel="blink"
|
||||
action="leds"
|
||||
extraParameters={{ duration: 10, pattern: 'on' }}
|
||||
/>
|
||||
<FirmwareUpgradeModal show={showUpgradeModal} toggleModal={setShowUpgradeModal} />
|
||||
</CCard>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
Device Actions
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow>
|
||||
<CCol>
|
||||
<CButton block onClick = { toggleRebootModal } color="primary">Reboot</CButton>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CButton block onClick = { toggleBlinkModal } color="primary">Blink</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow style={{marginTop :'10px'}}>
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick = { toggleUpgradeModal }>Firmware Upgrade</CButton>
|
||||
</CCol>
|
||||
<CCol>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CCardBody>
|
||||
<ActionModalWidget
|
||||
show={showRebootModal}
|
||||
toggleModal={toggleRebootModal}
|
||||
title='Reboot Device'
|
||||
directions='When would you like to reboot this device?'
|
||||
actionLabel='reboot'
|
||||
action='reboot'
|
||||
/>
|
||||
<ActionModalWidget
|
||||
show={showBlinkModal}
|
||||
toggleModal={toggleBlinkModal}
|
||||
title='Blink LEDs of Device'
|
||||
directions='When would you like make the LEDs of this device blink?'
|
||||
actionLabel='blink'
|
||||
action='leds'
|
||||
extraParameters= {{ duration : 10, pattern : 'on' }}
|
||||
/>
|
||||
<FirmwareUpgradeModal
|
||||
show={showUpgradeModal}
|
||||
toggleModal={setShowUpgradeModal}
|
||||
/>
|
||||
|
||||
{/*<CModal
|
||||
show={showModal}
|
||||
onClose={toggleModal}
|
||||
>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>Firmware Upgrade</CModalTitle>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<p>Insert the link of the firmware version you would like to upgrade the device to.</p>
|
||||
<CForm action="" method="post" encType="multipart/form-data" className="form-horizontal">
|
||||
<CInput
|
||||
className={'form-control', {'is-invalid' : !validField}}
|
||||
type="text"
|
||||
id="uri"
|
||||
name="uri-input"
|
||||
placeholder="https://somelocation.com/file=newversion.bin"
|
||||
autoComplete="firmware-uri"
|
||||
onChange={event => formChange(event.target.value)}
|
||||
/>
|
||||
<CInvalidFeedback>You need a url...</CInvalidFeedback>
|
||||
</CForm>
|
||||
</CModalBody>
|
||||
<CModalFooter>
|
||||
<CButton color="primary" onClick={event => formValidation()}>Upgrade</CButton>
|
||||
<CButton
|
||||
color="secondary"
|
||||
onClick={toggleModal}
|
||||
>
|
||||
Cancel
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
</CModal>*/}
|
||||
</CCard>
|
||||
);
|
||||
}
|
||||
|
||||
export default DeviceActions
|
||||
export default DeviceActions;
|
||||
|
||||
@@ -1,178 +1,169 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
CCard,
|
||||
CCardHeader,
|
||||
CCardBody,
|
||||
CFormGroup,
|
||||
CCol,
|
||||
CLabel,
|
||||
CForm,
|
||||
CInput,
|
||||
CCollapse,
|
||||
CCardFooter,
|
||||
CButton
|
||||
} from '@coreui/react'
|
||||
import CIcon from '@coreui/icons-react'
|
||||
CCard,
|
||||
CCardHeader,
|
||||
CCardBody,
|
||||
CFormGroup,
|
||||
CCol,
|
||||
CLabel,
|
||||
CForm,
|
||||
CInput,
|
||||
CCollapse,
|
||||
CCardFooter,
|
||||
CButton,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { cleanTimestamp } from '../utils/helper';
|
||||
import axiosInstance from '../utils/axiosInstance';
|
||||
import { getToken } from '../utils/authHelper';
|
||||
|
||||
|
||||
const DeviceConfiguration = () => {
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
const [device, setDevice] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const selectedDeviceId = useSelector(state => state.selectedDeviceId);
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
const [device, setDevice] = useState(null);
|
||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
||||
|
||||
const getDevice = () => {
|
||||
const options = {
|
||||
headers : {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${getToken()}`
|
||||
}
|
||||
};
|
||||
const getDevice = () => {
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance.get(`/device/${selectedDeviceId}`, options)
|
||||
.then((response) => {
|
||||
setDevice(response.data);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(error => {
|
||||
setLoading(false);
|
||||
console.log(error);
|
||||
console.log(error.response);
|
||||
});
|
||||
}
|
||||
axiosInstance
|
||||
.get(`/device/${selectedDeviceId}`, options)
|
||||
.then((response) => {
|
||||
setDevice(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
console.log(error.response);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getDevice();
|
||||
},[]);
|
||||
useEffect(() => {
|
||||
getDevice();
|
||||
}, []);
|
||||
|
||||
const toggle = (e) => {
|
||||
setCollapse(!collapse);
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if(device){
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
#{device.serialNumber} Details
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CForm action="" method="post" encType="multipart/form-data" className="form-horizontal">
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>UUID : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.UUID }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Serial Number : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.serialNumber }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Device Type : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ device.deviceType }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Last Configuration Change : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{cleanTimestamp(device.lastConfigurationChange) }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>MAC address :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ device.macAddress }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CCollapse show={collapse}>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Created : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ cleanTimestamp(device.createdTimestamp) }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Last Configuration Download : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{cleanTimestamp(device.lastConfigurationDownload) }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Manufacturer :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ device.manufacturer }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel htmlFor="text-input">Notes :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
<CInput id="text-input" name="text-input" placeholder={device.notes} />
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Owner :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ device.owner }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Location :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ device.location }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
</CCollapse>
|
||||
<CCardFooter>
|
||||
<CButton show={collapse} color="transparent" onClick = { toggle } block>
|
||||
<CIcon name={collapse ? "cilChevronTop" : "cilChevronBottom"} size="lg"/>
|
||||
</CButton>
|
||||
</CCardFooter>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
);
|
||||
}
|
||||
const toggle = (e) => {
|
||||
setCollapse(!collapse);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
if (device) {
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
Device Configuration
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
<CCard>
|
||||
<CCardHeader>#{device.serialNumber} Details</CCardHeader>
|
||||
<CCardBody>
|
||||
<CForm action="" method="post" encType="multipart/form-data" className="form-horizontal">
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>UUID : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.UUID}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Serial Number : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.serialNumber}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Device Type : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.deviceType}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Last Configuration Change : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{cleanTimestamp(device.lastConfigurationChange)}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>MAC address :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.macAddress}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CCollapse show={collapse}>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Created : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{cleanTimestamp(device.createdTimestamp)}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Last Configuration Download : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{cleanTimestamp(device.lastConfigurationDownload)}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Manufacturer :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.manufacturer}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel htmlFor="text-input">Notes :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
<CInput id="text-input" name="text-input" placeholder={device.notes} />
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Owner :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.owner}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Location :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.location}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
</CCollapse>
|
||||
<CCardFooter>
|
||||
<CButton show={collapse} color="transparent" onClick={toggle} block>
|
||||
<CIcon name={collapse ? 'cilChevronTop' : 'cilChevronBottom'} size="lg" />
|
||||
</CButton>
|
||||
</CCardFooter>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DeviceConfiguration
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>Device Configuration</CCardHeader>
|
||||
<CCardBody />
|
||||
</CCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeviceConfiguration;
|
||||
|
||||
@@ -1,168 +1,147 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import {
|
||||
CWidgetProgress,
|
||||
CCollapse,
|
||||
CButton,
|
||||
CDataTable,
|
||||
CCard,
|
||||
CCardBody
|
||||
} from '@coreui/react'
|
||||
import CIcon from '@coreui/icons-react'
|
||||
/* eslint-disable-rule prefer-destructuring */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { CWidgetProgress, CCollapse, CButton, CDataTable, CCard, CCardBody } from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { cleanTimestamp } from '../utils/helper';
|
||||
import axiosInstance from '../utils/axiosInstance';
|
||||
import { getToken } from '../utils/authHelper';
|
||||
|
||||
const DeviceHealth = () => {
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [details, setDetails] = useState([]);
|
||||
const [healthChecks, setHealthChecks] = useState([]);
|
||||
const selectedDeviceId = useSelector(state => state.selectedDeviceId);
|
||||
let sanityLevel;
|
||||
let barColor;
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
const [details, setDetails] = useState([]);
|
||||
const [healthChecks, setHealthChecks] = useState([]);
|
||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
||||
let sanityLevel;
|
||||
let barColor;
|
||||
|
||||
const toggle = (e) => {
|
||||
setCollapse(!collapse);
|
||||
e.preventDefault();
|
||||
const toggle = (e) => {
|
||||
setCollapse(!collapse);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const getDeviceHealth = () => {
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.get(`/device/${selectedDeviceId}/healthchecks`, options)
|
||||
.then((response) => {
|
||||
setHealthChecks(response.data.values);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
console.log(error.response);
|
||||
});
|
||||
};
|
||||
|
||||
// Function called from the button on the table so that a user can see more details
|
||||
const toggleDetails = (index) => {
|
||||
const position = details.indexOf(index);
|
||||
let newDetails = details.slice();
|
||||
|
||||
if (position !== -1) {
|
||||
newDetails.splice(position, 1);
|
||||
} else {
|
||||
newDetails = [...details, index];
|
||||
}
|
||||
setDetails(newDetails);
|
||||
};
|
||||
|
||||
const getDeviceHealth = () => {
|
||||
const options = {
|
||||
headers : {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${getToken()}`
|
||||
}
|
||||
};
|
||||
const columns = [
|
||||
{ key: 'UUID', label: 'Config. Id' },
|
||||
{ key: 'recorded' },
|
||||
{ key: 'sanity' },
|
||||
{
|
||||
key: 'show_details',
|
||||
label: '',
|
||||
_style: { width: '1%' },
|
||||
sorter: false,
|
||||
filter: false,
|
||||
},
|
||||
];
|
||||
|
||||
axiosInstance.get(`/device/${selectedDeviceId}/healthchecks`, options)
|
||||
.then((response) => {
|
||||
setHealthChecks(response.data.values);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(error => {
|
||||
setLoading(false);
|
||||
console.log(error);
|
||||
console.log(error.response);
|
||||
});
|
||||
}
|
||||
useEffect(() => {
|
||||
getDeviceHealth();
|
||||
}, []);
|
||||
|
||||
//Function called from the button on the table so that a user can see more details
|
||||
const toggleDetails = (index) => {
|
||||
const position = details.indexOf(index)
|
||||
let newDetails = details.slice()
|
||||
|
||||
if (position !== -1) {
|
||||
newDetails.splice(position, 1)
|
||||
}
|
||||
else {
|
||||
newDetails = [...details, index]
|
||||
}
|
||||
setDetails(newDetails)
|
||||
}
|
||||
if (healthChecks && healthChecks.length > 0) {
|
||||
sanityLevel = healthChecks[0].sanity;
|
||||
if (sanityLevel === 100) barColor = 'gradient-success';
|
||||
else if (sanityLevel >= 90) barColor = 'gradient-warning';
|
||||
else barColor = 'gradient-danger';
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{ key: 'UUID', label: 'Config. Id'},
|
||||
{ key: 'recorded'},
|
||||
{ key: 'sanity'},
|
||||
{
|
||||
key: 'show_details',
|
||||
label: '',
|
||||
_style: { width: '1%' },
|
||||
sorter: false,
|
||||
filter: false
|
||||
}
|
||||
];
|
||||
return (
|
||||
<CWidgetProgress
|
||||
header={sanityLevel ? `${sanityLevel}%` : 'Unknown'}
|
||||
text="Device Health"
|
||||
value={sanityLevel ?? 100}
|
||||
color={barColor}
|
||||
inverse
|
||||
footer={
|
||||
<div>
|
||||
<CCollapse show={collapse}>
|
||||
<CCard>
|
||||
<div className="overflow-auto" style={{ height: '250px' }}>
|
||||
<CDataTable
|
||||
items={healthChecks ?? []}
|
||||
fields={columns}
|
||||
style={{ color: 'white' }}
|
||||
sorterValue={{ column: 'recorded', desc: 'true' }}
|
||||
scopedSlots={{
|
||||
recorded: (item) => <td>{cleanTimestamp(item.recorded)}</td>,
|
||||
sanity: (item) => <td>{`${item.sanity}%`}</td>,
|
||||
show_details: (item, index) => {
|
||||
if (item.sanity === 100) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<td className="py-2">
|
||||
<CButton
|
||||
color="primary"
|
||||
variant="outline"
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDetails(index);
|
||||
}}
|
||||
>
|
||||
{details.includes(index) ? 'Hide' : 'Show'}
|
||||
</CButton>
|
||||
</td>
|
||||
);
|
||||
},
|
||||
details: (item, index) => (
|
||||
<CCollapse show={details.includes(index)}>
|
||||
<CCardBody>
|
||||
<h5>Details</h5>
|
||||
<div>
|
||||
<pre>{JSON.stringify(item.values, null, 4)}</pre>
|
||||
</div>
|
||||
</CCardBody>
|
||||
</CCollapse>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</CCard>
|
||||
</CCollapse>
|
||||
<CButton show={collapse} color="transparent" onClick={toggle} block>
|
||||
<CIcon
|
||||
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
||||
style={{ color: 'white' }}
|
||||
size="lg"
|
||||
/>
|
||||
</CButton>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getDeviceHealth();
|
||||
},[]);
|
||||
|
||||
if(healthChecks && healthChecks.length > 0){
|
||||
sanityLevel = healthChecks[0].sanity;
|
||||
if(sanityLevel === 100)
|
||||
barColor = "gradient-success";
|
||||
else if (sanityLevel >= 90)
|
||||
barColor = "gradient-warning";
|
||||
else
|
||||
barColor = "gradient-danger";
|
||||
}
|
||||
|
||||
return (
|
||||
<CWidgetProgress
|
||||
header={sanityLevel ? `${sanityLevel}%` : 'Unknown'}
|
||||
text="Device Health"
|
||||
value={sanityLevel ?? 100}
|
||||
color={barColor}
|
||||
inverse
|
||||
footer={
|
||||
<div>
|
||||
<CCollapse show={collapse}>
|
||||
<CCard>
|
||||
<div className="overflow-auto" style={{height: '250px'}}>
|
||||
<CDataTable
|
||||
items={healthChecks ?? [] }
|
||||
fields={columns}
|
||||
style={{color: 'white'}}
|
||||
sorterValue={{column: 'recorded', desc:'true'}}
|
||||
scopedSlots = {{
|
||||
'recorded':
|
||||
(item)=>(
|
||||
<td>
|
||||
{cleanTimestamp(item.recorded)}
|
||||
</td>
|
||||
),
|
||||
'sanity':
|
||||
(item)=>(
|
||||
<td>
|
||||
{`${item.sanity}%`}
|
||||
</td>
|
||||
),
|
||||
'show_details':
|
||||
(item, index)=>{
|
||||
if(item.sanity === 100){
|
||||
return (<></>);
|
||||
}
|
||||
return (
|
||||
<td className="py-2">
|
||||
<CButton
|
||||
color="primary"
|
||||
variant="outline"
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={()=>{toggleDetails(index)}}
|
||||
>
|
||||
{details.includes(index) ? 'Hide' : 'Show'}
|
||||
</CButton>
|
||||
</td>
|
||||
);
|
||||
},
|
||||
'details':
|
||||
(item, index)=>{
|
||||
return (
|
||||
<CCollapse show={details.includes(index)}>
|
||||
<CCardBody>
|
||||
<h5>Details</h5>
|
||||
<div><pre>{JSON.stringify(item.values, null, 4)}</pre></div>
|
||||
</CCardBody>
|
||||
</CCollapse>
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
</CDataTable>
|
||||
</div>
|
||||
</CCard>
|
||||
</CCollapse>
|
||||
<CButton show={collapse} color="transparent" onClick = { toggle } block>
|
||||
<CIcon name={collapse ? "cilChevronTop" : "cilChevronBottom"} style={{color: 'white'}} size="lg"/>
|
||||
</CButton>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
</CWidgetProgress>
|
||||
);
|
||||
}
|
||||
|
||||
export default DeviceHealth
|
||||
export default DeviceHealth;
|
||||
|
||||
@@ -1,328 +1,275 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
CBadge,
|
||||
CCardBody,
|
||||
CDataTable,
|
||||
CButton,
|
||||
CLink,
|
||||
CCard,
|
||||
CCardHeader,
|
||||
CRow,
|
||||
CCol,
|
||||
CPopover
|
||||
} from '@coreui/react'
|
||||
CBadge,
|
||||
CCardBody,
|
||||
CDataTable,
|
||||
CButton,
|
||||
CLink,
|
||||
CCard,
|
||||
CCardHeader,
|
||||
CRow,
|
||||
CCol,
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
import ReactPaginate from 'react-paginate';
|
||||
import Select from 'react-select'
|
||||
import Select from 'react-select';
|
||||
import { cilSync, cilInfo } from '@coreui/icons';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { getToken } from '../utils/authHelper';
|
||||
import axiosInstance from '../utils/axiosInstance';
|
||||
import { cleanBytesString, cropStringWithEllipsis } from '../utils/helper';
|
||||
import iotIcon from '../assets/icons/iot.png'
|
||||
import iotIcon from '../assets/icons/iot.png';
|
||||
import internetSwitch from '../assets/icons/networkswitch.png';
|
||||
import { cilSync, cilInfo } from '@coreui/icons';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
|
||||
const DeviceList = () => {
|
||||
const [loadedSerials, setLoadedSerials] = useState(false);
|
||||
const [serialNumbers, setSerialNumbers] = useState([]);
|
||||
const [page, setPage] = useState(0);
|
||||
const [pageCount, setPageCount] = useState(0);
|
||||
const [devicesPerPage, setDevicesPerPage] = useState(10);
|
||||
const [devices, setDevices] = useState([]);
|
||||
const [details, setDetails] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loadedSerials, setLoadedSerials] = useState(false);
|
||||
const [serialNumbers, setSerialNumbers] = useState([]);
|
||||
const [page, setPage] = useState(0);
|
||||
const [pageCount, setPageCount] = useState(0);
|
||||
const [devicesPerPage, setDevicesPerPage] = useState(10);
|
||||
const [devices, setDevices] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const getSerialNumbers = () => {
|
||||
const token = getToken();
|
||||
setLoading(true);
|
||||
const getSerialNumbers = () => {
|
||||
const token = getToken();
|
||||
setLoading(true);
|
||||
|
||||
const headers = {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
};
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
|
||||
axiosInstance.get('/devices?serialOnly=true', {
|
||||
headers: headers
|
||||
})
|
||||
.then((response) => {
|
||||
setSerialNumbers(response.data.serialNumbers);
|
||||
setLoadedSerials(true);
|
||||
})
|
||||
.catch(error => {
|
||||
setLoading(false);
|
||||
console.log(error.response);
|
||||
});
|
||||
};
|
||||
axiosInstance
|
||||
.get('/devices?serialOnly=true', {
|
||||
headers,
|
||||
})
|
||||
.then((response) => {
|
||||
setSerialNumbers(response.data.serialNumbers);
|
||||
setLoadedSerials(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
console.log(error.response);
|
||||
});
|
||||
};
|
||||
|
||||
const getDeviceInformation = () => {
|
||||
const token = getToken();
|
||||
setLoading(true);
|
||||
const getDeviceInformation = () => {
|
||||
const token = getToken();
|
||||
setLoading(true);
|
||||
|
||||
const headers = {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
};
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
|
||||
const startIndex = page * devicesPerPage;
|
||||
const endIndex = parseInt(startIndex) + parseInt(devicesPerPage);
|
||||
const serialsToGet = serialNumbers.slice(startIndex, endIndex).join(',');
|
||||
|
||||
axiosInstance.get(`/devices?deviceWithStatus=true&select=${serialsToGet}`, {
|
||||
headers: headers
|
||||
})
|
||||
.then((response) => {
|
||||
setDevices(response.data.devicesWithStatus);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(error => {
|
||||
setLoading(false);
|
||||
console.log(error.response);
|
||||
});
|
||||
}
|
||||
const startIndex = page * devicesPerPage;
|
||||
const endIndex = parseInt(startIndex, 10) + parseInt(devicesPerPage, 10);
|
||||
const serialsToGet = serialNumbers.slice(startIndex, endIndex).join(',');
|
||||
|
||||
//Function called from the button on the table so that a user can see more details
|
||||
const toggleDetails = (index) => {
|
||||
const position = details.indexOf(index)
|
||||
let newDetails = details.slice()
|
||||
if (position !== -1) {
|
||||
newDetails.splice(position, 1)
|
||||
} else {
|
||||
newDetails = [...details, index]
|
||||
}
|
||||
setDetails(newDetails)
|
||||
}
|
||||
axiosInstance
|
||||
.get(`/devices?deviceWithStatus=true&select=${serialsToGet}`, {
|
||||
headers,
|
||||
})
|
||||
.then((response) => {
|
||||
setDevices(response.data.devicesWithStatus);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
console.log(error.response);
|
||||
});
|
||||
};
|
||||
|
||||
const updateDevicesPerPage = (value) => {
|
||||
setDevicesPerPage(value);
|
||||
}
|
||||
const updateDevicesPerPage = (value) => {
|
||||
setDevicesPerPage(value);
|
||||
};
|
||||
|
||||
const updatePageCount = ({ selected: selectedPage }) => {
|
||||
setPage(selectedPage);
|
||||
}
|
||||
const updatePageCount = ({ selected: selectedPage }) => {
|
||||
setPage(selectedPage);
|
||||
};
|
||||
|
||||
//Initial load
|
||||
useEffect(() => {
|
||||
getSerialNumbers();
|
||||
}, []);
|
||||
// Initial load
|
||||
useEffect(() => {
|
||||
getSerialNumbers();
|
||||
}, []);
|
||||
|
||||
//Updating the devices only if serial numbers, page number or devices per page changes
|
||||
useEffect(()=>{
|
||||
if(loadedSerials) getDeviceInformation();
|
||||
}, [serialNumbers, page, devicesPerPage, loadedSerials]);
|
||||
// Updating the devices only if serial numbers, page number or devices per page changes
|
||||
useEffect(() => {
|
||||
if (loadedSerials) getDeviceInformation();
|
||||
}, [serialNumbers, page, devicesPerPage, loadedSerials]);
|
||||
|
||||
useEffect(() => {
|
||||
if(loadedSerials) {
|
||||
const count = Math.ceil(serialNumbers.length / devicesPerPage);
|
||||
setPageCount(count);
|
||||
}
|
||||
}, [devicesPerPage, loadedSerials]);
|
||||
useEffect(() => {
|
||||
if (loadedSerials) {
|
||||
const count = Math.ceil(serialNumbers.length / devicesPerPage);
|
||||
setPageCount(count);
|
||||
}
|
||||
}, [devicesPerPage, loadedSerials]);
|
||||
|
||||
return (
|
||||
<DeviceListDisplay
|
||||
devices={devices}
|
||||
toggleDetails={toggleDetails}
|
||||
details={details}
|
||||
loading={loading}
|
||||
updateDevicesPerPage={updateDevicesPerPage}
|
||||
pageCount={pageCount}
|
||||
updatePage={updatePageCount}
|
||||
pageRangeDisplayed={5}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const DeviceListDisplay = ({ devices, toggleDetails, details, loading, updateDevicesPerPage, pageCount, updatePage }) => {
|
||||
const columns = [
|
||||
{ key: 'deviceType', label: '', filter: false, sorter: false, _style: { width: '5%' },},
|
||||
{ key: 'serialNumber', _style: { width: '5%' }},
|
||||
{ key: 'UUID', label: 'Config Id', _style: { width: '5%' }},
|
||||
{ key: 'firmware', filter: false, _style: { width: '20%' }, },
|
||||
{ key: 'manufacturer', filter: false, _style: { width: '20%' }, },
|
||||
{ key: 'txBytes', label: 'Tx', filter: false, _style: { width: '10%' } },
|
||||
{ key: 'rxBytes', label: 'Rx', filter: false, _style: { width: '10%' } },
|
||||
{ key: 'ipAddress', _style: { width: '20%' }},
|
||||
{
|
||||
key: 'show_details',
|
||||
label: '',
|
||||
_style: { width: '3%' },
|
||||
sorter: false,
|
||||
filter: false
|
||||
},
|
||||
{
|
||||
key: 'refresh',
|
||||
label: '',
|
||||
_style: { width: '2%' },
|
||||
sorter: false,
|
||||
filter: false
|
||||
}
|
||||
];
|
||||
|
||||
const selectOptions = [
|
||||
{ value: '10', label: '10' },
|
||||
{ value: '25', label: '25' },
|
||||
{ value: '50', label: '50' }
|
||||
]
|
||||
|
||||
|
||||
const getDeviceIcon = (deviceType) =>{
|
||||
if(deviceType === "AP_Default"){
|
||||
return <CIcon name="cilRouter" size="2xl" alt='AP'></CIcon>;
|
||||
}
|
||||
else if(deviceType === "IOT"){
|
||||
return <img src={iotIcon} style={{height:'32px', width:'32px'}} alt='IOT'/>;
|
||||
}
|
||||
else if(deviceType === "SWITCH"){
|
||||
return <img src={internetSwitch} style={{height:'32px', width:'32px'}} alt='SWITCH'/>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const getStatusBadge = (status) => {
|
||||
if(status){
|
||||
return 'success';
|
||||
}
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<CRow>
|
||||
<CCol>
|
||||
</CCol>
|
||||
<CCol xs={2}>
|
||||
<Select
|
||||
isClearable = { false }
|
||||
options = { selectOptions }
|
||||
defaultValue = {{ value : '10', label: '10' }}
|
||||
onChange={value => updateDevicesPerPage(value.value)}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CDataTable
|
||||
items={devices}
|
||||
fields={columns}
|
||||
border
|
||||
hover
|
||||
loading = {loading}
|
||||
scopedSlots = {{
|
||||
'deviceType':
|
||||
(item)=>(
|
||||
<td style={{textAlign: 'center'}}>
|
||||
<CPopover
|
||||
content={ item.connected ? 'Connected' : 'Not Connected' }
|
||||
placement="top"
|
||||
>
|
||||
<CBadge color={getStatusBadge(item.connected)}>
|
||||
{getDeviceIcon(item.deviceType) ?? item.deviceType}
|
||||
</CBadge>
|
||||
</CPopover>
|
||||
</td>
|
||||
),
|
||||
'firmware':
|
||||
(item)=>(
|
||||
<td>
|
||||
<CPopover
|
||||
content={ item.firmware ? item.firmware : 'N/A' }
|
||||
placement="top"
|
||||
>
|
||||
<p>
|
||||
{cropStringWithEllipsis(item.firmware, 22)}
|
||||
</p>
|
||||
</CPopover>
|
||||
</td>
|
||||
),
|
||||
'manufacturer':
|
||||
(item)=>(
|
||||
<td>
|
||||
<CPopover
|
||||
content={ item.manufacturer ? item.manufacturer : 'N/A' }
|
||||
placement="top"
|
||||
>
|
||||
<p>
|
||||
{cropStringWithEllipsis(item.manufacturer, 23)}
|
||||
</p>
|
||||
</CPopover>
|
||||
</td>
|
||||
),
|
||||
'txBytes':
|
||||
(item)=>(
|
||||
<td>
|
||||
{cleanBytesString(item.txBytes)}
|
||||
</td>
|
||||
),
|
||||
'rxBytes':
|
||||
(item)=>(
|
||||
<td>
|
||||
{cleanBytesString(item.rxBytes)}
|
||||
</td>
|
||||
),
|
||||
'ipAddress':
|
||||
(item)=>(
|
||||
<td>
|
||||
<CPopover
|
||||
content={ item.ipAddress ? item.ipAddress : 'N/A' }
|
||||
placement="top"
|
||||
>
|
||||
<p>
|
||||
{cropStringWithEllipsis(item.ipAddress, 22)}
|
||||
</p>
|
||||
</CPopover>
|
||||
</td>
|
||||
),
|
||||
'refresh':
|
||||
(item)=>(
|
||||
<td className="py-2">
|
||||
<CButton color="primary" variant="outline" size="sm">
|
||||
<CIcon name="cil-sync" content={cilSync} size="sm"/>
|
||||
</CButton>
|
||||
</td>
|
||||
),
|
||||
'show_details':
|
||||
(item, index)=>{
|
||||
return (
|
||||
<td className="py-2">
|
||||
<CLink
|
||||
className="c-subheader-nav-link"
|
||||
aria-current="page"
|
||||
to={() => `/devices/${item.serialNumber}`}
|
||||
>
|
||||
<CButton
|
||||
color="primary"
|
||||
variant="outline"
|
||||
shape="square"
|
||||
size="sm"
|
||||
>
|
||||
<CIcon name="cil-info" content={cilInfo} size="l"/>
|
||||
</CButton>
|
||||
</CLink>
|
||||
</td>
|
||||
)
|
||||
}
|
||||
}}/>
|
||||
<ReactPaginate
|
||||
previousLabel={"← Previous"}
|
||||
nextLabel={"Next →"}
|
||||
pageCount={pageCount}
|
||||
onPageChange={updatePage}
|
||||
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'}
|
||||
/>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</>
|
||||
<DeviceListDisplay
|
||||
devices={devices}
|
||||
loading={loading}
|
||||
updateDevicesPerPage={updateDevicesPerPage}
|
||||
pageCount={pageCount}
|
||||
updatePage={updatePageCount}
|
||||
pageRangeDisplayed={5}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const DeviceListDisplay = ({ devices, loading, updateDevicesPerPage, pageCount, updatePage }) => {
|
||||
const columns = [
|
||||
{ key: 'deviceType', label: '', filter: false, sorter: false, _style: { width: '5%' } },
|
||||
{ key: 'serialNumber', _style: { width: '5%' } },
|
||||
{ key: 'UUID', label: 'Config Id', _style: { width: '5%' } },
|
||||
{ key: 'firmware', filter: false, _style: { width: '20%' } },
|
||||
{ key: 'manufacturer', filter: false, _style: { width: '20%' } },
|
||||
{ key: 'txBytes', label: 'Tx', filter: false, _style: { width: '10%' } },
|
||||
{ key: 'rxBytes', label: 'Rx', filter: false, _style: { width: '10%' } },
|
||||
{ key: 'ipAddress', _style: { width: '20%' } },
|
||||
{
|
||||
key: 'show_details',
|
||||
label: '',
|
||||
_style: { width: '3%' },
|
||||
sorter: false,
|
||||
filter: false,
|
||||
},
|
||||
{
|
||||
key: 'refresh',
|
||||
label: '',
|
||||
_style: { width: '2%' },
|
||||
sorter: false,
|
||||
filter: false,
|
||||
},
|
||||
];
|
||||
|
||||
export default DeviceList;
|
||||
const selectOptions = [
|
||||
{ value: '10', label: '10' },
|
||||
{ value: '25', label: '25' },
|
||||
{ value: '50', label: '50' },
|
||||
];
|
||||
|
||||
const getDeviceIcon = (deviceType) => {
|
||||
if (deviceType === 'AP_Default') {
|
||||
return <CIcon name="cilRouter" size="2xl" alt="AP" />;
|
||||
}
|
||||
if (deviceType === 'IOT') {
|
||||
return <img src={iotIcon} style={{ height: '32px', width: '32px' }} alt="IOT" />;
|
||||
}
|
||||
if (deviceType === 'SWITCH') {
|
||||
return <img src={internetSwitch} style={{ height: '32px', width: '32px' }} alt="SWITCH" />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const getStatusBadge = (status) => {
|
||||
if (status) {
|
||||
return 'success';
|
||||
}
|
||||
return 'danger';
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<CRow>
|
||||
<CCol />
|
||||
<CCol xs={2}>
|
||||
<Select
|
||||
isClearable={false}
|
||||
options={selectOptions}
|
||||
defaultValue={{ value: '10', label: '10' }}
|
||||
onChange={(value) => updateDevicesPerPage(value.value)}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CDataTable
|
||||
items={devices}
|
||||
fields={columns}
|
||||
border
|
||||
hover
|
||||
loading={loading}
|
||||
scopedSlots={{
|
||||
deviceType: (item) => (
|
||||
<td style={{ textAlign: 'center' }}>
|
||||
<CPopover
|
||||
content={item.connected ? 'Connected' : 'Not Connected'}
|
||||
placement="top"
|
||||
>
|
||||
<CBadge color={getStatusBadge(item.connected)}>
|
||||
{getDeviceIcon(item.deviceType) ?? item.deviceType}
|
||||
</CBadge>
|
||||
</CPopover>
|
||||
</td>
|
||||
),
|
||||
firmware: (item) => (
|
||||
<td>
|
||||
<CPopover content={item.firmware ? item.firmware : 'N/A'} placement="top">
|
||||
<p>{cropStringWithEllipsis(item.firmware, 22)}</p>
|
||||
</CPopover>
|
||||
</td>
|
||||
),
|
||||
manufacturer: (item) => (
|
||||
<td>
|
||||
<CPopover content={item.manufacturer ? item.manufacturer : 'N/A'} placement="top">
|
||||
<p>{cropStringWithEllipsis(item.manufacturer, 23)}</p>
|
||||
</CPopover>
|
||||
</td>
|
||||
),
|
||||
txBytes: (item) => <td>{cleanBytesString(item.txBytes)}</td>,
|
||||
rxBytes: (item) => <td>{cleanBytesString(item.rxBytes)}</td>,
|
||||
ipAddress: (item) => (
|
||||
<td>
|
||||
<CPopover content={item.ipAddress ? item.ipAddress : 'N/A'} placement="top">
|
||||
<p>{cropStringWithEllipsis(item.ipAddress, 22)}</p>
|
||||
</CPopover>
|
||||
</td>
|
||||
),
|
||||
refresh: () => (
|
||||
<td className="py-2">
|
||||
<CButton color="primary" variant="outline" size="sm">
|
||||
<CIcon name="cil-sync" content={cilSync} size="sm" />
|
||||
</CButton>
|
||||
</td>
|
||||
),
|
||||
show_details: (item) => (
|
||||
<td className="py-2">
|
||||
<CLink
|
||||
className="c-subheader-nav-link"
|
||||
aria-current="page"
|
||||
to={() => `/devices/${item.serialNumber}`}
|
||||
>
|
||||
<CButton color="primary" variant="outline" shape="square" size="sm">
|
||||
<CIcon name="cil-info" content={cilInfo} size="l" />
|
||||
</CButton>
|
||||
</CLink>
|
||||
</td>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<ReactPaginate
|
||||
previousLabel="← Previous"
|
||||
nextLabel="Next →"
|
||||
pageCount={pageCount}
|
||||
onPageChange={updatePage}
|
||||
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"
|
||||
/>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeviceList;
|
||||
|
||||
@@ -1,168 +1,160 @@
|
||||
import React from 'react'
|
||||
import React from 'react';
|
||||
import {
|
||||
CCard,
|
||||
CCardHeader,
|
||||
CCardBody,
|
||||
CFormGroup,
|
||||
CCol,
|
||||
CLabel,
|
||||
CForm,
|
||||
CInput,
|
||||
CCollapse,
|
||||
CCardFooter,
|
||||
CButton
|
||||
} from '@coreui/react'
|
||||
CCard,
|
||||
CCardHeader,
|
||||
CCardBody,
|
||||
CFormGroup,
|
||||
CCol,
|
||||
CLabel,
|
||||
CForm,
|
||||
CInput,
|
||||
CCollapse,
|
||||
CCardFooter,
|
||||
CButton,
|
||||
} from '@coreui/react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
const DeviceStatus = () => {
|
||||
let device = useSelector(state => state.selectedDevice);
|
||||
const device = useSelector((state) => state.selectedDevice);
|
||||
|
||||
if(device){
|
||||
let barColor = device.connected ? 'gradient-success' : 'gradient-warning';
|
||||
|
||||
return (
|
||||
<CWidgetProgressIcon
|
||||
header={sanityLevel ? `${sanityLevel}%` : 'Unknown'}
|
||||
text="Device Health"
|
||||
value={sanityLevel ?? 100}
|
||||
color={barColor}
|
||||
inverse
|
||||
>
|
||||
<CDropdown>
|
||||
{/* Need inline styling because CDropdownToggle does not take into account the
|
||||
parent's inverse value*/}
|
||||
<CDropdownToggle style={{color: 'white'}}>
|
||||
<CIcon content={cilSettings}/>
|
||||
</CDropdownToggle>
|
||||
<CDropdownMenu placement="bottom-end">
|
||||
<CDropdownItem>View Logs</CDropdownItem>
|
||||
</CDropdownMenu>
|
||||
</CDropdown>
|
||||
</CWidgetProgressIcon>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if(device){
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
Device #{device.serialNumber} Configuration
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CForm action="" method="post" encType="multipart/form-data" className="form-horizontal">
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>UUID : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.UUID }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Serial Number : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.serialNumber }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Device Type : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ device.deviceType }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Last Configuration Change : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{cleanTimestamp(device.lastConfigurationChange) }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>MAC address :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ device.macAddress }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CCollapse show={collapse}>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Created : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ cleanTimestamp(device.createdTimestamp) }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Last Configuration Download : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{cleanTimestamp(device.lastConfigurationDownload) }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Manufacturer :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ device.manufacturer }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel htmlFor="text-input">Notes :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
<CInput id="text-input" name="text-input" placeholder={device.notes} />
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Owner :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ device.owner }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Location :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{ device.location }
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
</CCollapse>
|
||||
<CCardFooter>
|
||||
<CButton
|
||||
color="primary"
|
||||
onClick={toggle}
|
||||
className={'mb-1'}
|
||||
>More details</CButton>
|
||||
</CCardFooter>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
);
|
||||
}
|
||||
if (device) {
|
||||
const barColor = device.connected ? 'gradient-success' : 'gradient-warning';
|
||||
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
Device Configuration
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
<CWidgetProgressIcon
|
||||
header={sanityLevel ? `${sanityLevel}%` : 'Unknown'}
|
||||
text="Device Health"
|
||||
value={sanityLevel ?? 100}
|
||||
color={barColor}
|
||||
inverse
|
||||
>
|
||||
<CDropdown>
|
||||
{/* Need inline styling because CDropdownToggle does not take into account the
|
||||
parent's inverse value */}
|
||||
<CDropdownToggle style={{ color: 'white' }}>
|
||||
<CIcon content={cilSettings} />
|
||||
</CDropdownToggle>
|
||||
<CDropdownMenu placement="bottom-end">
|
||||
<CDropdownItem>View Logs</CDropdownItem>
|
||||
</CDropdownMenu>
|
||||
</CDropdown>
|
||||
</CWidgetProgressIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DeviceStatus;
|
||||
if (device) {
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>Device #{device.serialNumber} Configuration</CCardHeader>
|
||||
<CCardBody>
|
||||
<CForm action="" method="post" encType="multipart/form-data" className="form-horizontal">
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>UUID : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.UUID}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Serial Number : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.serialNumber}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Device Type : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.deviceType}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Last Configuration Change : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{cleanTimestamp(device.lastConfigurationChange)}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>MAC address :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.macAddress}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CCollapse show={collapse}>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Created : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{cleanTimestamp(device.createdTimestamp)}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Last Configuration Download : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{cleanTimestamp(device.lastConfigurationDownload)}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Manufacturer :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.manufacturer}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel htmlFor="text-input">Notes :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
<CInput id="text-input" name="text-input" placeholder={device.notes} />
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Owner :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.owner}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol md="3">
|
||||
<CLabel>Location :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.location}
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
</CCollapse>
|
||||
<CCardFooter>
|
||||
<CButton color="primary" onClick={toggle} className="mb-1">
|
||||
More details
|
||||
</CButton>
|
||||
</CCardFooter>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>Device Configuration</CCardHeader>
|
||||
<CCardBody />
|
||||
</CCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeviceStatus;
|
||||
|
||||
@@ -1,209 +1,208 @@
|
||||
import {
|
||||
CButton,
|
||||
CModal,
|
||||
CModalHeader,
|
||||
CModalTitle,
|
||||
CModalBody,
|
||||
CModalFooter,
|
||||
CSpinner,
|
||||
CBadge,
|
||||
CCol,
|
||||
CRow,
|
||||
CInput,
|
||||
CInvalidFeedback
|
||||
CButton,
|
||||
CModal,
|
||||
CModalHeader,
|
||||
CModalTitle,
|
||||
CModalBody,
|
||||
CModalFooter,
|
||||
CSpinner,
|
||||
CBadge,
|
||||
CCol,
|
||||
CRow,
|
||||
CInput,
|
||||
CInvalidFeedback,
|
||||
} from '@coreui/react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { convertDateToUtc, convertDateFromUtc } from '../utils/helper'
|
||||
import DatePicker from "react-widgets/DatePicker";
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
import { useSelector } from 'react-redux';
|
||||
import "react-widgets/styles.css";
|
||||
import { convertDateToUtc, convertDateFromUtc } from '../utils/helper';
|
||||
import 'react-widgets/styles.css';
|
||||
import { getToken } from '../utils/authHelper';
|
||||
import axiosInstance from '../utils/axiosInstance';
|
||||
|
||||
const FirmwareUpgradeModal = ({ show, toggleModal }) => {
|
||||
const [hadSuccess, setHadSuccess] = useState(false);
|
||||
const [hadFailure, setHadFailure] = useState(false);
|
||||
const [waiting, setWaiting] = useState(false);
|
||||
const [chosenDate, setChosenDate] = useState(null);
|
||||
const [firmware, setFirmware] = useState('');
|
||||
const [validFirmware, setValidFirmware] = useState(true);
|
||||
const [responseBody, setResponseBody] = useState('');
|
||||
const [checkingIfSure, setCheckingIfSure] = useState(false);
|
||||
const selectedDeviceId = useSelector(state => state.selectedDeviceId);
|
||||
|
||||
const formValidation = () => {
|
||||
if (firmware.trim() === ''){
|
||||
setValidFirmware(false);
|
||||
return false;
|
||||
}
|
||||
return (chosenDate !== null);
|
||||
}
|
||||
const [hadSuccess, setHadSuccess] = useState(false);
|
||||
const [hadFailure, setHadFailure] = useState(false);
|
||||
const [waiting, setWaiting] = useState(false);
|
||||
const [chosenDate, setChosenDate] = useState(null);
|
||||
const [firmware, setFirmware] = useState('');
|
||||
const [validFirmware, setValidFirmware] = useState(true);
|
||||
const [responseBody, setResponseBody] = useState('');
|
||||
const [checkingIfSure, setCheckingIfSure] = useState(false);
|
||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
||||
|
||||
const setDateToNow = () => {
|
||||
const now = (new Date()).toISOString();
|
||||
setChosenDate(now);
|
||||
const formValidation = () => {
|
||||
if (firmware.trim() === '') {
|
||||
setValidFirmware(false);
|
||||
return false;
|
||||
}
|
||||
return chosenDate !== null;
|
||||
};
|
||||
|
||||
const setDateToLate = () => {
|
||||
const date = convertDateToUtc(new Date());
|
||||
if (date.getHours() >= 3){
|
||||
date.setDate(date.getDate() + 1);
|
||||
}
|
||||
date.setHours(3);
|
||||
date.setMinutes(0);
|
||||
const setDateToNow = () => {
|
||||
const now = new Date().toISOString();
|
||||
setChosenDate(now);
|
||||
};
|
||||
|
||||
setChosenDate(convertDateFromUtc(date).toISOString());
|
||||
const setDateToLate = () => {
|
||||
const date = convertDateToUtc(new Date());
|
||||
if (date.getHours() >= 3) {
|
||||
date.setDate(date.getDate() + 1);
|
||||
}
|
||||
date.setHours(3);
|
||||
date.setMinutes(0);
|
||||
|
||||
const setDate = (date) => {
|
||||
if(date){
|
||||
setChosenDate(date.toISOString());
|
||||
}
|
||||
}
|
||||
setChosenDate(convertDateFromUtc(date).toISOString());
|
||||
};
|
||||
|
||||
const confirmingIfSure = () => {
|
||||
setCheckingIfSure(true);
|
||||
const setDate = (date) => {
|
||||
if (date) {
|
||||
setChosenDate(date.toISOString());
|
||||
}
|
||||
useEffect(() => {
|
||||
setHadSuccess(false);
|
||||
setHadFailure(false);
|
||||
setWaiting(false);
|
||||
setChosenDate(null);
|
||||
setFirmware('');
|
||||
setValidFirmware(true);
|
||||
setResponseBody('');
|
||||
};
|
||||
|
||||
const confirmingIfSure = () => {
|
||||
setCheckingIfSure(true);
|
||||
};
|
||||
useEffect(() => {
|
||||
setHadSuccess(false);
|
||||
setHadFailure(false);
|
||||
setWaiting(false);
|
||||
setChosenDate(null);
|
||||
setFirmware('');
|
||||
setValidFirmware(true);
|
||||
setResponseBody('');
|
||||
setCheckingIfSure(false);
|
||||
}, [show]);
|
||||
|
||||
useEffect(() => {
|
||||
setValidFirmware(true);
|
||||
}, [firmware]);
|
||||
|
||||
const postUpgrade = () => {
|
||||
setHadFailure(false);
|
||||
setHadSuccess(false);
|
||||
setWaiting(true);
|
||||
|
||||
const token = getToken();
|
||||
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
serialNumber: selectedDeviceId,
|
||||
};
|
||||
|
||||
const parameters = {
|
||||
serialNumber: selectedDeviceId,
|
||||
when: chosenDate,
|
||||
uri: firmware,
|
||||
};
|
||||
axiosInstance
|
||||
.post(`/device/${selectedDeviceId}/upgrade`, parameters, { headers })
|
||||
.then((response) => {
|
||||
setResponseBody(JSON.stringify(response.data, null, 4));
|
||||
setHadSuccess(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
setHadFailure(true);
|
||||
console.log(error);
|
||||
console.log(error.response);
|
||||
})
|
||||
.finally(() => {
|
||||
setCheckingIfSure(false);
|
||||
},[show]);
|
||||
setWaiting(false);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setValidFirmware(true);
|
||||
},[firmware]);
|
||||
return (
|
||||
<CModal show={show} onClose={toggleModal}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>Firmware Upgrade</CModalTitle>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<h6>Choose a time and a firmware version for this device</h6>
|
||||
<CRow style={{ marginTop: '20px' }}>
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={() => setDateToNow()}>
|
||||
Now
|
||||
</CButton>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={() => setDateToLate()}>
|
||||
Later tonight
|
||||
</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow style={{ marginTop: '20px' }}>
|
||||
<CCol md="4" style={{ marginTop: '7px' }}>
|
||||
<p>Local time:</p>
|
||||
</CCol>
|
||||
<CCol xs="12" md="8">
|
||||
<DatePicker
|
||||
selected={Date.parse(chosenDate)}
|
||||
includeTime
|
||||
selectTime
|
||||
placeholder="Select custom date in UTC"
|
||||
disabled={waiting}
|
||||
onChange={(date) => setDate(date)}
|
||||
min={new Date()}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
|
||||
const postUpgrade = () => {
|
||||
setHadFailure(false);
|
||||
setHadSuccess(false);
|
||||
setWaiting(true);
|
||||
|
||||
const token = getToken();
|
||||
|
||||
const headers = {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'serialNumber': selectedDeviceId
|
||||
};
|
||||
|
||||
const parameters = {
|
||||
serialNumber: selectedDeviceId,
|
||||
when: chosenDate,
|
||||
uri: firmware
|
||||
}
|
||||
axiosInstance.post(`/device/${selectedDeviceId}/upgrade`, parameters,{ headers: headers})
|
||||
.then((response) => {
|
||||
setResponseBody(JSON.stringify(response.data, null, 4));
|
||||
setHadSuccess(true);
|
||||
})
|
||||
.catch(error => {
|
||||
setHadFailure(true);
|
||||
console.log(error);
|
||||
console.log(error.response);
|
||||
})
|
||||
.finally (() => {
|
||||
setCheckingIfSure(false);
|
||||
setWaiting(false);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<CModal
|
||||
show={show}
|
||||
onClose={toggleModal}
|
||||
<div style={{ marginTop: '25px' }}>
|
||||
<p>
|
||||
Device will upgrade at (UTC): <b>{chosenDate}</b>
|
||||
</p>
|
||||
</div>
|
||||
<CInput
|
||||
disabled={waiting}
|
||||
className={('form-control', { 'is-invalid': !validFirmware })}
|
||||
type="text"
|
||||
id="uri"
|
||||
name="uri-input"
|
||||
placeholder="https://somelocation.com/file=newversion.bin"
|
||||
autoComplete="firmware-uri"
|
||||
onChange={(event) => setFirmware(event.target.value)}
|
||||
value={firmware}
|
||||
/>
|
||||
<CInvalidFeedback>You need a url...</CInvalidFeedback>
|
||||
<div hidden={!hadSuccess && !hadFailure}>
|
||||
<div>
|
||||
<pre>{responseBody}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</CModalBody>
|
||||
<CModalFooter>
|
||||
<div hidden={!checkingIfSure}>Are you sure?</div>
|
||||
<CButton
|
||||
hidden={checkingIfSure}
|
||||
color="primary"
|
||||
onClick={() => (formValidation() ? confirmingIfSure() : null)}
|
||||
>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>Firmware Upgrade</CModalTitle>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<h6>Choose a time and a firmware version for this device</h6>
|
||||
<CRow style={{marginTop: '20px'}}>
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={ () => setDateToNow() } >Now</CButton>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={ () => setDateToLate() }>Later tonight</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow style={{marginTop: '20px'}}>
|
||||
<CCol md="4" style={{marginTop: '7px'}}>
|
||||
<p>Local time:</p>
|
||||
</CCol>
|
||||
<CCol xs="12" md="8">
|
||||
<DatePicker
|
||||
selected = { Date.parse(chosenDate) }
|
||||
includeTime
|
||||
selectTime
|
||||
placeholder = 'Select custom date in UTC'
|
||||
disabled = {waiting}
|
||||
onChange = {date => setDate(date)}
|
||||
min = { new Date() }
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
Schedule Upgrade
|
||||
</CButton>
|
||||
<CButton
|
||||
hidden={!checkingIfSure}
|
||||
disabled={waiting}
|
||||
color="primary"
|
||||
onClick={() => (formValidation() ? postUpgrade() : null)}
|
||||
>
|
||||
{waiting ? 'Loading...' : 'Yes'} {' '}
|
||||
<CSpinner hidden={!waiting} component="span" size="sm" />
|
||||
<CBadge hidden={waiting || !hadSuccess} color="success" shape="rounded-pill">
|
||||
Success
|
||||
</CBadge>
|
||||
<CBadge hidden={waiting || !hadFailure} color="danger" shape="rounded-pill">
|
||||
Request Failed
|
||||
</CBadge>
|
||||
</CButton>
|
||||
<CButton color="secondary" onClick={toggleModal}>
|
||||
Cancel
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
</CModal>
|
||||
);
|
||||
};
|
||||
|
||||
<div style={{marginTop : '25px'}}>
|
||||
<p>Device will upgrade at (UTC): <b>{ chosenDate }</b></p>
|
||||
</div>
|
||||
<CInput
|
||||
disabled = { waiting }
|
||||
className = {'form-control', {'is-invalid' : !validFirmware}}
|
||||
type = "text"
|
||||
id="uri"
|
||||
name="uri-input"
|
||||
placeholder="https://somelocation.com/file=newversion.bin"
|
||||
autoComplete="firmware-uri"
|
||||
onChange={event => setFirmware(event.target.value)}
|
||||
value={firmware}
|
||||
/>
|
||||
<CInvalidFeedback>You need a url...</CInvalidFeedback>
|
||||
<div hidden={(!hadSuccess && !hadFailure)}>
|
||||
<div><pre>{responseBody}</pre></div>
|
||||
</div>
|
||||
</CModalBody>
|
||||
<CModalFooter>
|
||||
<div hidden= { !checkingIfSure }>
|
||||
Are you sure?
|
||||
</div>
|
||||
<CButton
|
||||
hidden = { checkingIfSure }
|
||||
color="primary"
|
||||
onClick={event => formValidation() ? confirmingIfSure() : null}
|
||||
>
|
||||
Schedule Upgrade
|
||||
</CButton>
|
||||
<CButton
|
||||
hidden = { !checkingIfSure }
|
||||
disabled={ waiting }
|
||||
color="primary"
|
||||
onClick={event => formValidation() ? postUpgrade() : null}
|
||||
>
|
||||
{ waiting ? 'Loading...' : 'Yes'} {' '}
|
||||
<CSpinner hidden={ !waiting } component="span" size="sm"/>
|
||||
<CBadge hidden = { (waiting || !hadSuccess) } color="success" shape="rounded-pill">
|
||||
Success
|
||||
</CBadge>
|
||||
<CBadge hidden = { (waiting || !hadFailure) }color="danger" shape="rounded-pill">
|
||||
Request Failed
|
||||
</CBadge>
|
||||
</CButton>
|
||||
<CButton
|
||||
color="secondary"
|
||||
onClick={ toggleModal }
|
||||
>
|
||||
Cancel
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
|
||||
</CModal>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
export default FirmwareUpgradeModal;
|
||||
export default FirmwareUpgradeModal;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"REACT_APP_BASE_URL" : "https://ucentral.dpaas.arilia.com:16001/api/v1"
|
||||
}
|
||||
"REACT_APP_BASE_URL": "https://ucentral.dpaas.arilia.com:16001/api/v1"
|
||||
}
|
||||
|
||||
@@ -1,45 +1,42 @@
|
||||
import React, { Suspense } from 'react'
|
||||
import {
|
||||
Redirect,
|
||||
Route,
|
||||
Switch
|
||||
} from 'react-router-dom'
|
||||
import { CContainer, CFade } from '@coreui/react'
|
||||
import routes from '../routes'
|
||||
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
import React, { Suspense } from 'react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import { v4 as createUuid } from 'uuid';
|
||||
import { CContainer, CFade } from '@coreui/react';
|
||||
import routes from '../routes';
|
||||
|
||||
const loading = (
|
||||
<div className="pt-3 text-center">
|
||||
<div className="sk-spinner sk-spinner-pulse"></div>
|
||||
<div className="sk-spinner sk-spinner-pulse" />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
const TheContent = () => {
|
||||
return (
|
||||
<main className="c-main">
|
||||
<CContainer fluid>
|
||||
<Suspense fallback={loading}>
|
||||
<Switch>
|
||||
{
|
||||
routes.map((route, idx) => {
|
||||
return route.component && (
|
||||
const TheContent = () => (
|
||||
<main className="c-main">
|
||||
<CContainer fluid>
|
||||
<Suspense fallback={loading}>
|
||||
<Switch>
|
||||
{routes.map(
|
||||
(route) =>
|
||||
route.component && (
|
||||
<Route
|
||||
key={idx}
|
||||
key={createUuid()}
|
||||
path={route.path}
|
||||
exact={route.exact}
|
||||
name={route.name}
|
||||
render={props => (
|
||||
render={(props) => (
|
||||
<CFade>
|
||||
<route.component {...props} />
|
||||
</CFade>
|
||||
)} />
|
||||
)
|
||||
})}
|
||||
<Redirect from="/" to="/devices" />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</CContainer>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
)}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
<Redirect from="/" to="/devices" />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</CContainer>
|
||||
</main>
|
||||
);
|
||||
|
||||
export default React.memo(TheContent)
|
||||
export default React.memo(TheContent);
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import React from 'react'
|
||||
import { CFooter } from '@coreui/react'
|
||||
import React from 'react';
|
||||
import { CFooter } from '@coreui/react';
|
||||
|
||||
const TheFooter = () => {
|
||||
return (
|
||||
<CFooter fixed={false}>
|
||||
<div>
|
||||
<a href="https://coreui.io" target="_blank" rel="noopener noreferrer">CoreUI</a>
|
||||
<span className="ml-1">© 2020 creativeLabs.</span>
|
||||
</div>
|
||||
<div className="mfs-auto">
|
||||
<span className="mr-1">Powered by</span>
|
||||
<a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer">CoreUI for React</a>
|
||||
</div>
|
||||
</CFooter>
|
||||
)
|
||||
}
|
||||
const TheFooter = () => (
|
||||
<CFooter fixed={false}>
|
||||
<div>
|
||||
<a href="https://coreui.io" target="_blank" rel="noopener noreferrer">
|
||||
CoreUI
|
||||
</a>
|
||||
<span className="ml-1">© 2020 creativeLabs.</span>
|
||||
</div>
|
||||
<div className="mfs-auto">
|
||||
<span className="mr-1">Powered by</span>
|
||||
<a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer">
|
||||
CoreUI for React
|
||||
</a>
|
||||
</div>
|
||||
</CFooter>
|
||||
);
|
||||
|
||||
export default React.memo(TheFooter)
|
||||
export default React.memo(TheFooter);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { logout } from '../utils/authHelper'
|
||||
import React from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import {
|
||||
CHeader,
|
||||
CToggler,
|
||||
@@ -8,71 +7,65 @@ import {
|
||||
CHeaderNav,
|
||||
CSubheader,
|
||||
CBreadcrumbRouter,
|
||||
CLink
|
||||
} from '@coreui/react'
|
||||
import CIcon from '@coreui/icons-react'
|
||||
import { cilAccountLogout} from '@coreui/icons';
|
||||
import routes from '../routes'
|
||||
CLink,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilAccountLogout } from '@coreui/icons';
|
||||
import { logout } from '../utils/authHelper';
|
||||
import routes from '../routes';
|
||||
|
||||
const TheHeader = () => {
|
||||
const dispatch = useDispatch()
|
||||
const sidebarShow = useSelector(state => state.sidebarShow)
|
||||
const dispatch = useDispatch();
|
||||
const sidebarShow = useSelector((state) => state.sidebarShow);
|
||||
|
||||
const toggleSidebar = () => {
|
||||
const val = [true, 'responsive'].includes(sidebarShow) ? false : 'responsive'
|
||||
dispatch({type: 'set', sidebarShow: val})
|
||||
}
|
||||
const val = [true, 'responsive'].includes(sidebarShow) ? false : 'responsive';
|
||||
dispatch({ type: 'set', sidebarShow: val });
|
||||
};
|
||||
|
||||
const toggleSidebarMobile = () => {
|
||||
const val = [false, 'responsive'].includes(sidebarShow) ? true : 'responsive'
|
||||
dispatch({type: 'set', sidebarShow: val})
|
||||
}
|
||||
const val = [false, 'responsive'].includes(sidebarShow) ? true : 'responsive';
|
||||
dispatch({ type: 'set', sidebarShow: val });
|
||||
};
|
||||
|
||||
return (
|
||||
<CHeader withSubheader>
|
||||
<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-md-3 d-lg-none" onClick={toggleSidebarMobile} />
|
||||
<CToggler inHeader className="ml-3 d-md-down-none" onClick={toggleSidebar} />
|
||||
<CHeaderBrand className="mx-auto d-lg-none" to="/">
|
||||
<CIcon name="logo" height="48" alt="Logo"/>
|
||||
<CIcon name="logo" height="48" alt="Logo" />
|
||||
</CHeaderBrand>
|
||||
|
||||
<CHeaderNav className="d-md-down-none mr-auto">
|
||||
</CHeaderNav>
|
||||
<CHeaderNav className="d-md-down-none mr-auto" />
|
||||
|
||||
<CHeaderNav className="px-3">
|
||||
<CLink className="c-subheader-nav-link">
|
||||
<CIcon name="cilAccountLogout" content={cilAccountLogout} size="2xl" onClick={() => { logout();}} />
|
||||
<CIcon
|
||||
name="cilAccountLogout"
|
||||
content={cilAccountLogout}
|
||||
size="2xl"
|
||||
onClick={() => {
|
||||
logout();
|
||||
}}
|
||||
/>
|
||||
</CLink>
|
||||
</CHeaderNav>
|
||||
|
||||
<CSubheader className="px-3 justify-content-between">
|
||||
<CBreadcrumbRouter
|
||||
className="border-0 c-subheader-nav m-0 px-0 px-md-3"
|
||||
routes={routes}
|
||||
/>
|
||||
<div className="d-md-down-none mfe-2 c-subheader-nav">
|
||||
<CLink
|
||||
className="c-subheader-nav-link"
|
||||
aria-current="page"
|
||||
to="/devices"
|
||||
>
|
||||
<CIcon name="cil-graph" alt="Dashboard" /> Dashboard
|
||||
</CLink>
|
||||
<CLink className="c-subheader-nav-link" href="#">
|
||||
<CIcon name="cil-settings" alt="Settings" /> Settings
|
||||
</CLink>
|
||||
</div>
|
||||
<CBreadcrumbRouter className="border-0 c-subheader-nav m-0 px-0 px-md-3" routes={routes} />
|
||||
<div className="d-md-down-none mfe-2 c-subheader-nav">
|
||||
<CLink className="c-subheader-nav-link" aria-current="page" to="/devices">
|
||||
<CIcon name="cil-graph" alt="Dashboard" />
|
||||
Dashboard
|
||||
</CLink>
|
||||
<CLink className="c-subheader-nav-link" href="#">
|
||||
<CIcon name="cil-settings" alt="Settings" />
|
||||
Settings
|
||||
</CLink>
|
||||
</div>
|
||||
</CSubheader>
|
||||
</CHeader>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default TheHeader
|
||||
export default TheHeader;
|
||||
|
||||
@@ -1,33 +1,27 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
TheContent,
|
||||
TheSidebar,
|
||||
TheFooter,
|
||||
TheHeader
|
||||
} from './index';
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import TheContent from './TheContent';
|
||||
import TheSidebar from './TheSidebar';
|
||||
import TheFooter from './TheFooter';
|
||||
import TheHeader from './TheHeader';
|
||||
|
||||
const TheLayout = (props) => {
|
||||
const { isLoggedIn } = useSelector(state => state.connected);
|
||||
if(isLoggedIn){
|
||||
return (
|
||||
<div>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
const { isLoggedIn } = useSelector((state) => state.connected);
|
||||
if (isLoggedIn) {
|
||||
return <div>{props.children}</div>;
|
||||
}
|
||||
return (
|
||||
<div className="c-app c-default-layout">
|
||||
<TheSidebar/>
|
||||
<TheSidebar />
|
||||
<div className="c-wrapper">
|
||||
<TheHeader/>
|
||||
<TheHeader />
|
||||
<div className="c-body">
|
||||
<TheContent/>
|
||||
<TheContent />
|
||||
</div>
|
||||
<TheFooter/>
|
||||
<TheFooter />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default TheLayout
|
||||
export default TheLayout;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import React from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import {
|
||||
CCreateElement,
|
||||
CSidebar,
|
||||
@@ -10,49 +10,37 @@ import {
|
||||
CSidebarMinimizer,
|
||||
CSidebarNavDropdown,
|
||||
CSidebarNavItem,
|
||||
} from '@coreui/react'
|
||||
} from '@coreui/react';
|
||||
|
||||
import CIcon from '@coreui/icons-react'
|
||||
import CIcon from '@coreui/icons-react';
|
||||
|
||||
// sidebar nav config
|
||||
import navigation from './_nav'
|
||||
import navigation from './_nav';
|
||||
|
||||
const TheSidebar = () => {
|
||||
const dispatch = useDispatch()
|
||||
const show = useSelector(state => state.sidebarShow)
|
||||
const dispatch = useDispatch();
|
||||
const show = useSelector((state) => state.sidebarShow);
|
||||
|
||||
return (
|
||||
<CSidebar
|
||||
show={show}
|
||||
onShowChange={(val) => dispatch({type: 'set', sidebarShow: val })}
|
||||
>
|
||||
<CSidebar show={show} onShowChange={(val) => dispatch({ type: 'set', sidebarShow: val })}>
|
||||
<CSidebarBrand className="d-md-down-none" to="/devices">
|
||||
<CIcon
|
||||
className="c-sidebar-brand-full"
|
||||
name="logo-negative"
|
||||
height={35}
|
||||
/>
|
||||
<CIcon
|
||||
className="c-sidebar-brand-minimized"
|
||||
name="sygnet"
|
||||
height={35}
|
||||
/>
|
||||
<CIcon className="c-sidebar-brand-full" name="logo-negative" height={35} />
|
||||
<CIcon className="c-sidebar-brand-minimized" name="sygnet" height={35} />
|
||||
</CSidebarBrand>
|
||||
<CSidebarNav>
|
||||
|
||||
<CCreateElement
|
||||
items={navigation}
|
||||
components={{
|
||||
CSidebarNavDivider,
|
||||
CSidebarNavDropdown,
|
||||
CSidebarNavItem,
|
||||
CSidebarNavTitle
|
||||
CSidebarNavTitle,
|
||||
}}
|
||||
/>
|
||||
</CSidebarNav>
|
||||
<CSidebarMinimizer className="c-d-md-down-none"/>
|
||||
<CSidebarMinimizer className="c-d-md-down-none" />
|
||||
</CSidebar>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(TheSidebar)
|
||||
export default React.memo(TheSidebar);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const _nav = [
|
||||
const nav = [
|
||||
{
|
||||
_tag: 'CSidebarNavItem',
|
||||
name: 'List of Devices',
|
||||
to: '/devices',
|
||||
icon: 'cilNotes'
|
||||
icon: 'cilNotes',
|
||||
},
|
||||
{
|
||||
_tag: 'CSidebarNavItem',
|
||||
@@ -17,6 +17,6 @@ const _nav = [
|
||||
to: '/Settings',
|
||||
icon: 'cil-settings',
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
export default _nav
|
||||
export default nav;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import TheContent from './TheContent'
|
||||
import TheFooter from './TheFooter'
|
||||
import TheHeader from './TheHeader'
|
||||
import TheLayout from './TheLayout'
|
||||
import TheSidebar from './TheSidebar'
|
||||
|
||||
export {
|
||||
TheContent,
|
||||
TheFooter,
|
||||
TheHeader,
|
||||
TheLayout,
|
||||
TheSidebar
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
|
||||
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
|
||||
12
src/index.js
12
src/index.js
@@ -1,19 +1,19 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App.js';
|
||||
import store from './store';
|
||||
import { Provider } from 'react-redux';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||
import { icons } from './assets/icons'
|
||||
import App from './App';
|
||||
import store from './store';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import { icons } from './assets/icons';
|
||||
|
||||
React.icons = icons;
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<Provider store={store}>
|
||||
<App/>
|
||||
<App />
|
||||
</Provider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
document.getElementById('root'),
|
||||
);
|
||||
|
||||
@@ -4,10 +4,10 @@ const DevicePage = React.lazy(() => import('./views/pages/DevicePage'));
|
||||
const DeviceListPage = React.lazy(() => import('./views/pages/DeviceListPage'));
|
||||
|
||||
const routes = [
|
||||
{ path: '/devices', exact: true, name: 'Devices', component: DeviceListPage },
|
||||
{ path: '/devices/:deviceId', name: 'Device Page', component: DevicePage },
|
||||
{ path: '/Device', name: 'Device', component: DevicePage },
|
||||
{ path: '/page2', name: 'Page2', component: DeviceListPage, exact: true },
|
||||
{ path: '/devices', exact: true, name: 'Devices', component: DeviceListPage },
|
||||
{ path: '/devices/:deviceId', name: 'Device Page', component: DevicePage },
|
||||
{ path: '/Device', name: 'Device', component: DevicePage },
|
||||
{ path: '/page2', name: 'Page2', component: DeviceListPage, exact: true },
|
||||
];
|
||||
|
||||
export default routes;
|
||||
export default routes;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// If you want to override variables do it here
|
||||
@import "variables";
|
||||
@import 'variables';
|
||||
|
||||
// Import CoreUI styles
|
||||
@import "~@coreui/coreui/scss/coreui.scss";
|
||||
@import '~@coreui/coreui/scss/coreui.scss';
|
||||
|
||||
// Some temp fixes
|
||||
@import "fixes";
|
||||
@import 'fixes';
|
||||
|
||||
// If you want to add something do it here
|
||||
@import "custom";
|
||||
@import 'custom';
|
||||
|
||||
@@ -15,9 +15,7 @@ const isLocalhost = Boolean(
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.0/8 are considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/),
|
||||
);
|
||||
|
||||
export function register(config) {
|
||||
@@ -43,7 +41,7 @@ export function register(config) {
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA',
|
||||
);
|
||||
});
|
||||
} else {
|
||||
@@ -71,7 +69,7 @@ function registerValidSW(swUrl, config) {
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.',
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
@@ -122,9 +120,7 @@ function checkValidServiceWorker(swUrl, config) {
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
console.log('No internet connection found. App is running in offline mode.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
16
src/store.js
16
src/store.js
@@ -1,20 +1,20 @@
|
||||
import { createStore } from 'redux'
|
||||
import { createStore } from 'redux';
|
||||
|
||||
const initialState = {
|
||||
sidebarShow: 'responsive',
|
||||
connected: false,
|
||||
selectedDeviceId: null,
|
||||
selectedDevice: null
|
||||
}
|
||||
selectedDevice: null,
|
||||
};
|
||||
|
||||
const changeState = (state = initialState, { type, ...rest }) => {
|
||||
switch (type) {
|
||||
case 'set':
|
||||
return {...state, ...rest }
|
||||
return { ...state, ...rest };
|
||||
default:
|
||||
return state
|
||||
return state;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const store = createStore(changeState)
|
||||
export default store
|
||||
const store = createStore(changeState);
|
||||
export default store;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
export const logout = () => {
|
||||
sessionStorage.clear();
|
||||
window.location.replace('/');
|
||||
}
|
||||
sessionStorage.clear();
|
||||
window.location.replace('/');
|
||||
};
|
||||
|
||||
export const getToken = () => {
|
||||
const token = sessionStorage.getItem('access_token');
|
||||
if (token === undefined || token === null) {
|
||||
logout();
|
||||
return;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
const token = sessionStorage.getItem('access_token');
|
||||
if (token === undefined || token === null) {
|
||||
logout();
|
||||
return null;
|
||||
}
|
||||
return token;
|
||||
};
|
||||
|
||||
@@ -3,45 +3,46 @@ import axiosRetry from 'axios-retry';
|
||||
|
||||
const axiosInstance = axios.create();
|
||||
|
||||
axiosRetry(axiosInstance , {
|
||||
retries: 3,
|
||||
retryDelay: (retryCount) => {
|
||||
console.log(`retry attempt: ${retryCount}`);
|
||||
return axiosRetry.exponentialDelay;
|
||||
},
|
||||
axiosRetry(axiosInstance, {
|
||||
retries: 3,
|
||||
retryDelay: (retryCount) => {
|
||||
console.log(`retry attempt: ${retryCount}`);
|
||||
return axiosRetry.exponentialDelay;
|
||||
},
|
||||
});
|
||||
|
||||
axiosInstance.defaults.headers.get['Accept'] = 'application/json' // default header for all get request
|
||||
axiosInstance.defaults.headers.post['Accept'] = 'application/json' // default header for all POST request
|
||||
axiosInstance.defaults.headers.get.Accept = 'application/json';
|
||||
axiosInstance.defaults.headers.post.Accept = 'application/json';
|
||||
|
||||
axiosInstance.interceptors.request.use(function(config) {
|
||||
const url = sessionStorage.getItem('gw_url');
|
||||
if(url !== undefined && url !== null && !config.url.includes(url)){
|
||||
config.url = url + config.url;
|
||||
}
|
||||
return config;
|
||||
axiosInstance.interceptors.request.use((config) => {
|
||||
const newConfig = config;
|
||||
const url = sessionStorage.getItem('gw_url');
|
||||
if (url !== undefined && url !== null && !newConfig.url.includes(url)) {
|
||||
newConfig.url = url + newConfig.url;
|
||||
}
|
||||
return newConfig;
|
||||
});
|
||||
|
||||
axiosInstance.interceptors.response.use(
|
||||
//Success actions
|
||||
undefined,
|
||||
function(error) {
|
||||
console.log(error);
|
||||
switch(error.response.status){
|
||||
case 401:
|
||||
console.log('Error 401 ' + error );
|
||||
break;
|
||||
case 403:
|
||||
console.log('Error 403 ' + error );
|
||||
sessionStorage.clear();
|
||||
window.location.href = '/';
|
||||
break;
|
||||
default:
|
||||
console.log('Default ' + error.response.status);
|
||||
break;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
// Success actions
|
||||
undefined,
|
||||
(error) => {
|
||||
console.log(error);
|
||||
switch (error.response.status) {
|
||||
case 401:
|
||||
console.log(`Error 401 ${error}`);
|
||||
break;
|
||||
case 403:
|
||||
console.log(`Error 403 ${error}`);
|
||||
sessionStorage.clear();
|
||||
window.location.href = '/';
|
||||
break;
|
||||
default:
|
||||
console.log(`Default ${error.response.status}`);
|
||||
break;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
export default axiosInstance;
|
||||
export default axiosInstance;
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
export const cleanTimestamp = (timestamp) => {
|
||||
return timestamp.replace('T', ' ').replace('Z', ' ');
|
||||
}
|
||||
export const cleanTimestamp = (timestamp) => timestamp.replace('T', ' ').replace('Z', ' ');
|
||||
|
||||
export const cropStringWithEllipsis = (text, lengthWithEllipsis) => {
|
||||
if(!text || text.length === '') return 'N/A';
|
||||
if (!text || text.length === '') return 'N/A';
|
||||
|
||||
return text.length > lengthWithEllipsis ? text.substring(0, (lengthWithEllipsis - 3)) + '\u2026' : text;
|
||||
}
|
||||
return text.length > lengthWithEllipsis
|
||||
? `${text.substring(0, lengthWithEllipsis - 3)}\u2026`
|
||||
: text;
|
||||
};
|
||||
|
||||
export const cleanBytesString = (bytes, decimals = 2) => {
|
||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (!bytes || bytes === 0) {
|
||||
return '0 B';
|
||||
}
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(k)));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (!bytes || bytes === 0) {
|
||||
return '0 B';
|
||||
}
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(k)), 10);
|
||||
return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`;
|
||||
};
|
||||
|
||||
export const convertDateToUtc = (date) => {
|
||||
const utcDate = new Date(date.getTime() + date.getTimezoneOffset() * 60000);
|
||||
return utcDate;
|
||||
}
|
||||
const utcDate = new Date(date.getTime() + date.getTimezoneOffset() * 60000);
|
||||
return utcDate;
|
||||
};
|
||||
|
||||
export const convertDateFromUtc = (utcDate) => {
|
||||
const dateObj = new Date();
|
||||
const date = new Date(utcDate.getTime() - dateObj.getTimezoneOffset() * 60000);
|
||||
return date;
|
||||
}
|
||||
const dateObj = new Date();
|
||||
const date = new Date(utcDate.getTime() - dateObj.getTimezoneOffset() * 60000);
|
||||
return date;
|
||||
};
|
||||
|
||||
@@ -1,36 +1,29 @@
|
||||
import React from 'react'
|
||||
import { CCard, CCardBody, CCardHeader, CCol, CRow } from '@coreui/react'
|
||||
import CIcon from '@coreui/icons-react'
|
||||
import { brandSet } from '@coreui/icons'
|
||||
import { DocsLink } from 'src/reusable'
|
||||
import React from 'react';
|
||||
import { CCard, CCardBody, CCardHeader, CCol, CRow } from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { brandSet } from '@coreui/icons';
|
||||
import { DocsLink } from 'src/reusable';
|
||||
|
||||
const toKebabCase = (str) => {
|
||||
return str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase()
|
||||
}
|
||||
const toKebabCase = (str) => str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();
|
||||
|
||||
export const getIconsView = iconset => {
|
||||
return Object.entries(iconset).map(([name, value]) => (
|
||||
export const getIconsView = (iconset) =>
|
||||
Object.entries(iconset).map(([name, value]) => (
|
||||
<CCol className="mb-5" xs="6" sm="4" md="3" xl="2" key={name}>
|
||||
<CIcon content={value} size="2xl"/>
|
||||
<CIcon content={value} size="2xl" />
|
||||
<div>{toKebabCase(name)}</div>
|
||||
</CCol>
|
||||
))
|
||||
}
|
||||
));
|
||||
|
||||
const CoreUIIcons = () => {
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
Brand Icons
|
||||
<DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub"/>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow className="text-center">
|
||||
{getIconsView(brandSet)}
|
||||
</CRow>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
)
|
||||
}
|
||||
const CoreUIIcons = () => (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
Brand Icons
|
||||
<DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub" />
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow className="text-center">{getIconsView(brandSet)}</CRow>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
);
|
||||
|
||||
export default CoreUIIcons
|
||||
export default CoreUIIcons;
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
import React from 'react'
|
||||
import { CCard, CCardBody, CCardHeader, CRow } from '@coreui/react'
|
||||
import { freeSet } from '@coreui/icons'
|
||||
import { getIconsView } from '../brands/Brands.js'
|
||||
import { DocsLink } from 'src/reusable'
|
||||
import React from 'react';
|
||||
import { CCard, CCardBody, CCardHeader, CRow } from '@coreui/react';
|
||||
import { freeSet } from '@coreui/icons';
|
||||
import { DocsLink } from 'src/reusable';
|
||||
import { getIconsView } from '../brands/Brands.js';
|
||||
|
||||
const CoreUIIcons = () => {
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
Free Icons / as CIcon{' '}
|
||||
<DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub"/>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow className="text-center">
|
||||
{getIconsView(freeSet)}
|
||||
</CRow>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
)
|
||||
}
|
||||
const CoreUIIcons = () => (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
Free Icons / as CIcon <DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub" />
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow className="text-center">{getIconsView(freeSet)}</CRow>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
);
|
||||
|
||||
export default CoreUIIcons
|
||||
export default CoreUIIcons;
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
import React from 'react'
|
||||
import { CCard, CCardBody, CCardHeader, CRow } from '@coreui/react'
|
||||
import { getIconsView } from '../brands/Brands.js'
|
||||
import { flagSet } from '@coreui/icons'
|
||||
import { DocsLink } from 'src/reusable'
|
||||
import React from 'react';
|
||||
import { CCard, CCardBody, CCardHeader, CRow } from '@coreui/react';
|
||||
import { flagSet } from '@coreui/icons';
|
||||
import { DocsLink } from 'src/reusable';
|
||||
import { getIconsView } from '../brands/Brands.js';
|
||||
|
||||
const CoreUIIcons = () => {
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
Flag Icons
|
||||
<DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub"/>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow className="text-center">
|
||||
{getIconsView(flagSet)}
|
||||
</CRow>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
)
|
||||
}
|
||||
const CoreUIIcons = () => (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
Flag Icons
|
||||
<DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub" />
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow className="text-center">{getIconsView(flagSet)}</CRow>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
);
|
||||
|
||||
export default CoreUIIcons
|
||||
export default CoreUIIcons;
|
||||
|
||||
@@ -2,6 +2,4 @@ import CoreUIIcons from './coreui-icons';
|
||||
import Flags from './flags';
|
||||
import Brands from './brands';
|
||||
|
||||
export {
|
||||
CoreUIIcons, Flags, Brands
|
||||
};
|
||||
export { CoreUIIcons, Flags, Brands };
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React from 'react';
|
||||
import DeviceList from '../../components/DeviceList';
|
||||
|
||||
const DeviceListPage = (props) => {
|
||||
return (
|
||||
<div className="App">
|
||||
<DeviceList />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const DeviceListPage = () => (
|
||||
<div className="App">
|
||||
<DeviceList />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default DeviceListPage;
|
||||
export default DeviceListPage;
|
||||
|
||||
@@ -1,45 +1,42 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import React, { useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import {
|
||||
CRow,
|
||||
CCol
|
||||
} from '@coreui/react'
|
||||
import { CRow, CCol } from '@coreui/react';
|
||||
import DeviceHealth from '../../components/DeviceHealth';
|
||||
import DeviceConfiguration from '../../components/DeviceConfiguration';
|
||||
import DeviceActions from '../../components/DeviceActions'
|
||||
import DeviceActions from '../../components/DeviceActions';
|
||||
|
||||
const DevicePage = (props) => {
|
||||
const dispatch = useDispatch();
|
||||
const DevicePage = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
//Storing the deviceId in the store
|
||||
let selectedDeviceId = useSelector(state => state.selectedDeviceId);
|
||||
let { deviceId } = useParams();
|
||||
// Storing the deviceId in the store
|
||||
let selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
||||
const { deviceId } = useParams();
|
||||
|
||||
if(!selectedDeviceId || selectedDeviceId !== deviceId){
|
||||
dispatch({type: 'set', selectedDeviceId: deviceId});
|
||||
selectedDeviceId = deviceId;
|
||||
}
|
||||
if (!selectedDeviceId || selectedDeviceId !== deviceId) {
|
||||
dispatch({ type: 'set', selectedDeviceId: deviceId });
|
||||
selectedDeviceId = deviceId;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({type: 'set', selectedDevice: null, selectedDeviceId: null});
|
||||
},[]);
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'set', selectedDevice: null, selectedDeviceId: null });
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="App">
|
||||
<CRow>
|
||||
<CCol xs='12' sm='6'>
|
||||
<DeviceConfiguration/>
|
||||
</CCol>
|
||||
<CCol xs='12' sm='6'>
|
||||
<DeviceHealth/>
|
||||
<DeviceActions/>
|
||||
</CCol >
|
||||
</CRow>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="App">
|
||||
<CRow>
|
||||
<CCol xs="12" sm="6">
|
||||
<DeviceConfiguration />
|
||||
</CCol>
|
||||
<CCol xs="12" sm="6">
|
||||
<DeviceHealth />
|
||||
<DeviceActions />
|
||||
</CCol>
|
||||
</CRow>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DevicePage;
|
||||
export default DevicePage;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
CButton,
|
||||
CCard,
|
||||
@@ -14,147 +14,185 @@ import {
|
||||
CRow,
|
||||
CPopover,
|
||||
CAlert,
|
||||
CInvalidFeedback
|
||||
} from '@coreui/react'
|
||||
import CIcon from '@coreui/icons-react'
|
||||
import { cilUser, cilLockLocked, cilLink} from '@coreui/icons';
|
||||
CInvalidFeedback,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilUser, cilLockLocked, cilLink } from '@coreui/icons';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import axiosInstance from '../../utils/axiosInstance';
|
||||
|
||||
|
||||
const Login = () => {
|
||||
const dispatch = useDispatch();
|
||||
const [userId, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [gatewayUrl, setGatewayUrl] = useState('https://ucentral.dpaas.arilia.com:16001/api/v1');
|
||||
const [error, setError] = useState(false);
|
||||
const [emptyUsername, setEmptyUsername] = useState(false);
|
||||
const [emptyPassword, setEmptyPassword] = useState(false);
|
||||
const [emptyGateway, setEmptyGateway] = useState(false);
|
||||
|
||||
const onKeyDown = (event) => {
|
||||
if(event.code === 'Enter' && formValidation()){
|
||||
SignIn({ userId, password });
|
||||
}
|
||||
};
|
||||
const dispatch = useDispatch();
|
||||
const [userId, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [gatewayUrl, setGatewayUrl] = useState('https://ucentral.dpaas.arilia.com:16001/api/v1');
|
||||
const [hadError, setHadError] = useState(false);
|
||||
const [emptyUsername, setEmptyUsername] = useState(false);
|
||||
const [emptyPassword, setEmptyPassword] = useState(false);
|
||||
const [emptyGateway, setEmptyGateway] = useState(false);
|
||||
const placeholderUrl = 'Gateway URL (ex: https://ucentral.dpaas.arilia.com:16001/api/v1)';
|
||||
const loginErrorText =
|
||||
'Login error, confirm that your username, password and gateway url are valid';
|
||||
|
||||
const SignIn = (credentials) => {
|
||||
axiosInstance.post(`${gatewayUrl}/oauth2`, credentials)
|
||||
.then((response) => {
|
||||
sessionStorage.setItem('gw_url', gatewayUrl);
|
||||
sessionStorage.setItem('access_token', response.data.access_token);
|
||||
dispatch({type: 'set', connected: true});
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
setError(true);
|
||||
console.log(error.response);
|
||||
});
|
||||
};
|
||||
|
||||
const formValidation = () => {
|
||||
setError(false);
|
||||
const formValidation = () => {
|
||||
setHadError(false);
|
||||
|
||||
let isSuccesful = true;
|
||||
let isSuccesful = true;
|
||||
|
||||
if(userId.trim() === ''){
|
||||
setEmptyUsername(true);
|
||||
isSuccesful = false;
|
||||
}
|
||||
if (userId.trim() === '') {
|
||||
setEmptyUsername(true);
|
||||
isSuccesful = false;
|
||||
}
|
||||
|
||||
if(password.trim() === ''){
|
||||
setEmptyPassword(true);
|
||||
isSuccesful = false;
|
||||
}
|
||||
if (password.trim() === '') {
|
||||
setEmptyPassword(true);
|
||||
isSuccesful = false;
|
||||
}
|
||||
|
||||
if(gatewayUrl.trim() === ''){
|
||||
setEmptyGateway(true);
|
||||
isSuccesful = false;
|
||||
}
|
||||
|
||||
return isSuccesful;
|
||||
};
|
||||
if (gatewayUrl.trim() === '') {
|
||||
setEmptyGateway(true);
|
||||
isSuccesful = false;
|
||||
}
|
||||
|
||||
useEffect(() => { if (emptyUsername) setEmptyUsername(false); }, [userId]);
|
||||
useEffect(() => { if (emptyPassword) setEmptyPassword(false); }, [password]);
|
||||
useEffect(() => { if (emptyGateway) setEmptyGateway(false); }, [gatewayUrl]);
|
||||
return isSuccesful;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="c-app c-default-layout flex-row align-items-center">
|
||||
<CContainer>
|
||||
<CRow className="justify-content-center">
|
||||
<CCol md="8">
|
||||
<CCardGroup>
|
||||
<CCard className="p-4">
|
||||
<CCardBody>
|
||||
<CForm onKeyDown = { onKeyDown }>
|
||||
<h1>Login</h1>
|
||||
<p className="text-muted">Sign In to your account</p>
|
||||
<CInputGroup className="mb-3">
|
||||
<CPopover content="Username">
|
||||
<CInputGroupPrepend>
|
||||
<CInputGroupText>
|
||||
<CIcon name="cilUser" content={cilUser }/>
|
||||
</CInputGroupText>
|
||||
</CInputGroupPrepend>
|
||||
</CPopover>
|
||||
<CInput invalid = { emptyUsername } autoFocus required type="text" placeholder="Username" autoComplete="username" onChange={event => setUsername(event.target.value)}/>
|
||||
<CInvalidFeedback className="help-block">
|
||||
Please enter your username
|
||||
</CInvalidFeedback>
|
||||
</CInputGroup>
|
||||
<CInputGroup className="mb-4">
|
||||
<CPopover content="Password">
|
||||
<CInputGroupPrepend>
|
||||
<CInputGroupText>
|
||||
<CIcon name="cilLockLocked" content={ cilLockLocked }/>
|
||||
</CInputGroupText>
|
||||
</CInputGroupPrepend>
|
||||
</CPopover>
|
||||
<CInput invalid = { emptyPassword } type="password" required placeholder="Password" autoComplete="current-password" onChange={event => setPassword(event.target.value)} />
|
||||
<CInvalidFeedback className="help-block">
|
||||
Please enter your password
|
||||
</CInvalidFeedback>
|
||||
</CInputGroup>
|
||||
<CInputGroup className="mb-4">
|
||||
<CPopover content="Gateway URL">
|
||||
<CInputGroupPrepend>
|
||||
<CInputGroupText>
|
||||
<CIcon name="cilLink" content={ cilLink }/>
|
||||
</CInputGroupText>
|
||||
</CInputGroupPrepend>
|
||||
</CPopover>
|
||||
<CInput invalid = { emptyGateway } type="text" required placeholder="Gateway URL (ex: https://ucentral.dpaas.arilia.com:16001/api/v1)" value={gatewayUrl} autoComplete="gateway-url" onChange={event => setGatewayUrl(event.target.value)} />
|
||||
<CInvalidFeedback className="help-block">
|
||||
Please enter a gateway url
|
||||
</CInvalidFeedback>
|
||||
</CInputGroup>
|
||||
<CRow>
|
||||
<CCol>
|
||||
<CAlert show = { error } color="danger">
|
||||
Login error, confirm that your username, password and gateway url are valid
|
||||
</CAlert>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol xs="6">
|
||||
<CButton color="primary" className="px-4" onClick={event => formValidation() ? SignIn({ userId, password }) : null }>Login
|
||||
</CButton>
|
||||
</CCol>
|
||||
<CCol xs="6" className="text-right">
|
||||
<CButton color="link" className="px-0">Forgot password?</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</CCardGroup>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Login
|
||||
|
||||
const SignIn = (credentials) => {
|
||||
axiosInstance
|
||||
.post(`${gatewayUrl}/oauth2`, credentials)
|
||||
.then((response) => {
|
||||
sessionStorage.setItem('gw_url', gatewayUrl);
|
||||
sessionStorage.setItem('access_token', response.data.access_token);
|
||||
dispatch({ type: 'set', connected: true });
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
setHadError(true);
|
||||
console.log(error.response);
|
||||
});
|
||||
};
|
||||
|
||||
const onKeyDown = (event) => {
|
||||
if (event.code === 'Enter' && formValidation()) {
|
||||
SignIn({ userId, password });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (emptyUsername) setEmptyUsername(false);
|
||||
}, [userId]);
|
||||
useEffect(() => {
|
||||
if (emptyPassword) setEmptyPassword(false);
|
||||
}, [password]);
|
||||
useEffect(() => {
|
||||
if (emptyGateway) setEmptyGateway(false);
|
||||
}, [gatewayUrl]);
|
||||
|
||||
return (
|
||||
<div className="c-app c-default-layout flex-row align-items-center">
|
||||
<CContainer>
|
||||
<CRow className="justify-content-center">
|
||||
<CCol md="8">
|
||||
<CCardGroup>
|
||||
<CCard className="p-4">
|
||||
<CCardBody>
|
||||
<CForm onKeyDown={onKeyDown}>
|
||||
<h1>Login</h1>
|
||||
<p className="text-muted">Sign In to your account</p>
|
||||
<CInputGroup className="mb-3">
|
||||
<CPopover content="Username">
|
||||
<CInputGroupPrepend>
|
||||
<CInputGroupText>
|
||||
<CIcon name="cilUser" content={cilUser} />
|
||||
</CInputGroupText>
|
||||
</CInputGroupPrepend>
|
||||
</CPopover>
|
||||
<CInput
|
||||
invalid={emptyUsername}
|
||||
autoFocus
|
||||
required
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
autoComplete="username"
|
||||
onChange={(event) => setUsername(event.target.value)}
|
||||
/>
|
||||
<CInvalidFeedback className="help-block">
|
||||
Please enter your username
|
||||
</CInvalidFeedback>
|
||||
</CInputGroup>
|
||||
<CInputGroup className="mb-4">
|
||||
<CPopover content="Password">
|
||||
<CInputGroupPrepend>
|
||||
<CInputGroupText>
|
||||
<CIcon content={cilLockLocked} />
|
||||
</CInputGroupText>
|
||||
</CInputGroupPrepend>
|
||||
</CPopover>
|
||||
<CInput
|
||||
invalid={emptyPassword}
|
||||
required
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
autoComplete="current-password"
|
||||
onChange={(event) => setPassword(event.target.value)}
|
||||
/>
|
||||
<CInvalidFeedback className="help-block">
|
||||
Please enter your password
|
||||
</CInvalidFeedback>
|
||||
</CInputGroup>
|
||||
<CInputGroup className="mb-4">
|
||||
<CPopover content="Gateway URL">
|
||||
<CInputGroupPrepend>
|
||||
<CInputGroupText>
|
||||
<CIcon name="cilLink" content={cilLink} />
|
||||
</CInputGroupText>
|
||||
</CInputGroupPrepend>
|
||||
</CPopover>
|
||||
<CInput
|
||||
invalid={emptyGateway}
|
||||
type="text"
|
||||
required
|
||||
placeholder={placeholderUrl}
|
||||
value={gatewayUrl}
|
||||
autoComplete="gateway-url"
|
||||
onChange={(event) => setGatewayUrl(event.target.value)}
|
||||
/>
|
||||
<CInvalidFeedback className="help-block">
|
||||
Please enter a gateway url
|
||||
</CInvalidFeedback>
|
||||
</CInputGroup>
|
||||
<CRow>
|
||||
<CCol>
|
||||
<CAlert show={hadError} color="danger">
|
||||
{loginErrorText}
|
||||
</CAlert>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol xs="6">
|
||||
<CButton
|
||||
color="primary"
|
||||
className="px-4"
|
||||
onClick={() => (formValidation() ? SignIn({ userId, password }) : null)}
|
||||
>
|
||||
Login
|
||||
</CButton>
|
||||
</CCol>
|
||||
<CCol xs="6" className="text-right">
|
||||
<CButton color="link" className="px-0">
|
||||
Forgot password?
|
||||
</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</CCardGroup>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
||||
|
||||
@@ -1,168 +1,176 @@
|
||||
import {
|
||||
CButton,
|
||||
CModal,
|
||||
CModalHeader,
|
||||
CModalTitle,
|
||||
CModalBody,
|
||||
CModalFooter,
|
||||
CSpinner,
|
||||
CBadge,
|
||||
CCol,
|
||||
CRow
|
||||
CButton,
|
||||
CModal,
|
||||
CModalHeader,
|
||||
CModalTitle,
|
||||
CModalBody,
|
||||
CModalFooter,
|
||||
CSpinner,
|
||||
CBadge,
|
||||
CCol,
|
||||
CRow,
|
||||
} from '@coreui/react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { convertDateToUtc, convertDateFromUtc } from '../utils/helper'
|
||||
import DatePicker from "react-widgets/DatePicker";
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
import { useSelector } from 'react-redux';
|
||||
import "react-widgets/styles.css";
|
||||
import { convertDateToUtc, convertDateFromUtc } from '../utils/helper';
|
||||
import 'react-widgets/styles.css';
|
||||
import { getToken } from '../utils/authHelper';
|
||||
import axiosInstance from '../utils/axiosInstance';
|
||||
|
||||
const ActionModalWidget = ({ show, toggleModal, title, directions, actionLabel, action, extraParameters }) => {
|
||||
const [hadSuccess, setHadSuccess] = useState(false);
|
||||
const [hadFailure, setHadFailure] = useState(false);
|
||||
const [waiting, setWaiting] = useState(false);
|
||||
const [chosenDate, setChosenDate] = useState(null);
|
||||
const [responseBody, setResponseBody] = useState('');
|
||||
const selectedDeviceId = useSelector(state => state.selectedDeviceId);
|
||||
|
||||
const formValidation = () => {
|
||||
return (chosenDate !== null);
|
||||
const ActionModalWidget = ({
|
||||
show,
|
||||
toggleModal,
|
||||
title,
|
||||
directions,
|
||||
actionLabel,
|
||||
action,
|
||||
extraParameters,
|
||||
}) => {
|
||||
const [hadSuccess, setHadSuccess] = useState(false);
|
||||
const [hadFailure, setHadFailure] = useState(false);
|
||||
const [waiting, setWaiting] = useState(false);
|
||||
const [chosenDate, setChosenDate] = useState(null);
|
||||
const [responseBody, setResponseBody] = useState('');
|
||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
||||
|
||||
const formValidation = () => chosenDate !== null;
|
||||
|
||||
const setDateToNow = () => {
|
||||
const now = new Date().toISOString();
|
||||
setChosenDate(now);
|
||||
};
|
||||
|
||||
const setDateToLate = () => {
|
||||
const date = convertDateToUtc(new Date());
|
||||
if (date.getHours() >= 3) {
|
||||
date.setDate(date.getDate() + 1);
|
||||
}
|
||||
date.setHours(3);
|
||||
date.setMinutes(0);
|
||||
|
||||
const setDateToNow = () => {
|
||||
const now = (new Date()).toISOString();
|
||||
setChosenDate(now);
|
||||
setChosenDate(convertDateFromUtc(date).toISOString());
|
||||
};
|
||||
|
||||
const setDate = (date) => {
|
||||
if (date) {
|
||||
setChosenDate(convertDateFromUtc(date).toISOString());
|
||||
}
|
||||
};
|
||||
|
||||
const setDateToLate = () => {
|
||||
const date = convertDateToUtc(new Date());
|
||||
if (date.getHours() >= 3){
|
||||
date.setDate(date.getDate() + 1);
|
||||
}
|
||||
date.setHours(3);
|
||||
date.setMinutes(0);
|
||||
useEffect(() => {
|
||||
setHadSuccess(false);
|
||||
setHadFailure(false);
|
||||
setWaiting(false);
|
||||
setChosenDate(false);
|
||||
setResponseBody('');
|
||||
}, [show]);
|
||||
|
||||
setChosenDate(convertDateFromUtc(date).toISOString());
|
||||
}
|
||||
const doAction = () => {
|
||||
setHadFailure(false);
|
||||
setHadSuccess(false);
|
||||
setWaiting(true);
|
||||
|
||||
const setDate = (date) => {
|
||||
if(date){
|
||||
setChosenDate(convertDateFromUtc(date).toISOString());
|
||||
}
|
||||
}
|
||||
const token = getToken();
|
||||
|
||||
useEffect(() => {
|
||||
setHadSuccess(false);
|
||||
setHadFailure(false);
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
serialNumber: selectedDeviceId,
|
||||
};
|
||||
|
||||
const necessaryParameters = {
|
||||
serialNumber: selectedDeviceId,
|
||||
when: chosenDate,
|
||||
};
|
||||
|
||||
const parameters = { ...necessaryParameters, ...extraParameters };
|
||||
|
||||
axiosInstance
|
||||
.post(`/device/${selectedDeviceId}/${action}`, parameters, { headers })
|
||||
.then((response) => {
|
||||
setResponseBody(JSON.stringify(response.data, null, 4));
|
||||
setHadSuccess(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
setHadFailure(true);
|
||||
console.log(error);
|
||||
console.log(error.response);
|
||||
})
|
||||
.finally(() => {
|
||||
setWaiting(false);
|
||||
setChosenDate(false);
|
||||
setResponseBody('');
|
||||
},[show]);
|
||||
});
|
||||
};
|
||||
|
||||
const doAction = () => {
|
||||
setHadFailure(false);
|
||||
setHadSuccess(false);
|
||||
setWaiting(true);
|
||||
return (
|
||||
<CModal show={show} onClose={toggleModal}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>{title}</CModalTitle>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<h6>{directions}</h6>
|
||||
<CRow style={{ marginTop: '20px' }}>
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={() => setDateToNow()}>
|
||||
Now
|
||||
</CButton>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={() => setDateToLate()}>
|
||||
Later tonight
|
||||
</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow style={{ marginTop: '20px' }}>
|
||||
<CCol md="4" style={{ marginTop: '7px' }}>
|
||||
<p>Custom (UTC):</p>
|
||||
</CCol>
|
||||
<CCol xs="12" md="8">
|
||||
<DatePicker
|
||||
selected={Date.parse(chosenDate)}
|
||||
includeTime
|
||||
selectTime
|
||||
placeholder="Select custom date in UTC"
|
||||
disabled={waiting}
|
||||
onChange={(date) => setDate(date)}
|
||||
min={convertDateToUtc(new Date())}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
|
||||
const token = getToken();
|
||||
<div style={{ marginTop: '25px' }}>
|
||||
<p>
|
||||
Device will {actionLabel} at (UTC): <b>{chosenDate}</b>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
const headers = {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'serialNumber': selectedDeviceId
|
||||
};
|
||||
|
||||
const necessaryParameters = {
|
||||
serialNumber : selectedDeviceId,
|
||||
when: chosenDate
|
||||
}
|
||||
|
||||
const parameters = {...necessaryParameters, ...extraParameters };
|
||||
|
||||
axiosInstance.post(`/device/${selectedDeviceId}/${action}`, parameters,{ headers: headers})
|
||||
.then((response) => {
|
||||
setResponseBody(JSON.stringify(response.data, null, 4));
|
||||
setHadSuccess(true);
|
||||
})
|
||||
.catch(error => {
|
||||
setHadFailure(true);
|
||||
console.log(error);
|
||||
console.log(error.response);
|
||||
})
|
||||
.finally (() => {
|
||||
setWaiting(false);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<CModal
|
||||
show={show}
|
||||
onClose={toggleModal}
|
||||
<div hidden={!hadSuccess && !hadFailure}>
|
||||
<div>
|
||||
<pre>{responseBody}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</CModalBody>
|
||||
<CModalFooter>
|
||||
<CButton
|
||||
disabled={waiting}
|
||||
color="primary"
|
||||
onClick={() => (formValidation() ? doAction() : null)}
|
||||
>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>{ title }</CModalTitle>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<h6>{ directions }</h6>
|
||||
<CRow style={{marginTop: '20px'}}>
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={ () => setDateToNow() } >Now</CButton>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={ () => setDateToLate() }>Later tonight</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow style={{marginTop: '20px'}}>
|
||||
<CCol md="4" style={{marginTop: '7px'}}>
|
||||
<p>Custom (UTC):</p>
|
||||
</CCol>
|
||||
<CCol xs="12" md="8">
|
||||
<DatePicker
|
||||
selected = { Date.parse(chosenDate) }
|
||||
includeTime
|
||||
selectTime
|
||||
placeholder = 'Select custom date in UTC'
|
||||
disabled = {waiting}
|
||||
onChange = {date => setDate(date)}
|
||||
min = { convertDateToUtc(new Date()) }
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
{waiting ? 'Loading...' : 'Schedule'} {' '}
|
||||
<CSpinner hidden={!waiting} component="span" size="sm" />
|
||||
<CBadge hidden={waiting || !hadSuccess} color="success" shape="rounded-pill">
|
||||
Success
|
||||
</CBadge>
|
||||
<CBadge hidden={waiting || !hadFailure} color="danger" shape="rounded-pill">
|
||||
Request Failed
|
||||
</CBadge>
|
||||
</CButton>
|
||||
<CButton color="secondary" onClick={toggleModal}>
|
||||
Cancel
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
</CModal>
|
||||
);
|
||||
};
|
||||
|
||||
<div style={{marginTop : '25px'}}>
|
||||
<p>Device will { actionLabel } at (UTC): <b>{ chosenDate }</b></p>
|
||||
</div>
|
||||
|
||||
<div hidden={(!hadSuccess && !hadFailure)}>
|
||||
<div><pre>{responseBody}</pre></div>
|
||||
</div>
|
||||
</CModalBody>
|
||||
<CModalFooter>
|
||||
<CButton
|
||||
disabled={waiting}
|
||||
color="primary"
|
||||
onClick={event => formValidation() ? doAction() : null}
|
||||
>
|
||||
{ waiting ? 'Loading...' : 'Schedule'} {' '}
|
||||
<CSpinner hidden={ !waiting } component="span" size="sm"/>
|
||||
<CBadge hidden = { (waiting || !hadSuccess) } color="success" shape="rounded-pill">
|
||||
Success
|
||||
</CBadge>
|
||||
<CBadge hidden = { (waiting || !hadFailure) }color="danger" shape="rounded-pill">
|
||||
Request Failed
|
||||
</CBadge>
|
||||
</CButton>
|
||||
<CButton
|
||||
color="secondary"
|
||||
onClick={ toggleModal }
|
||||
>
|
||||
Cancel
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
</CModal>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
export default ActionModalWidget;
|
||||
export default ActionModalWidget;
|
||||
|
||||
Reference in New Issue
Block a user