mirror of
https://github.com/optim-enterprises-bv/OptimCloud-gw-ui.git
synced 2025-10-29 17:32:20 +00:00
Added commands and logs to device page
This commit is contained in:
183
src/components/DeviceCommands.js
Normal file
183
src/components/DeviceCommands.js
Normal file
@@ -0,0 +1,183 @@
|
||||
/* eslint-disable-rule prefer-destructuring */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
CWidgetDropdown,
|
||||
CRow,
|
||||
CCol,
|
||||
CCollapse,
|
||||
CButton,
|
||||
CDataTable,
|
||||
CCard,
|
||||
CCardBody,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
import { cleanTimestamp, addDays } from '../utils/helper';
|
||||
import axiosInstance from '../utils/axiosInstance';
|
||||
import { getToken } from '../utils/authHelper';
|
||||
|
||||
const DeviceCommands = () => {
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
const [details, setDetails] = useState([]);
|
||||
const [commands, setCommands] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [start, setStart] = useState(addDays(new Date(), -3).toString());
|
||||
const [end, setEnd] = useState(new Date().toString());
|
||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
||||
|
||||
const toggle = (e) => {
|
||||
setCollapse(!collapse);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const getCommands = () => {
|
||||
setLoading(true);
|
||||
const utcStart = new Date(start).toISOString();
|
||||
const utcEnd = new Date(end).toISOString();
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
},
|
||||
params: {
|
||||
startDate: utcStart,
|
||||
endDate: utcEnd,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.get(`/commands?serialNumber=${selectedDeviceId}`, options)
|
||||
.then((response) => {
|
||||
setCommands(response.data.commands);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
console.log(error.response);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
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 columns = [
|
||||
{ key: 'UUID', label: 'Id' },
|
||||
{ key: 'command' },
|
||||
{ key: 'completed' },
|
||||
{
|
||||
key: 'show_details',
|
||||
label: '',
|
||||
_style: { width: '1%' },
|
||||
sorter: false,
|
||||
filter: false,
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
getCommands();
|
||||
setStart(addDays(new Date(), -3).toString());
|
||||
setEnd(new Date().toString());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getCommands();
|
||||
}, [start, end]);
|
||||
|
||||
return (
|
||||
<CWidgetDropdown
|
||||
inverse
|
||||
color="gradient-primary"
|
||||
header="Device Commands"
|
||||
footerSlot={
|
||||
<div style={{ padding: '20px' }}>
|
||||
<CCollapse show={collapse}>
|
||||
<CRow style={{ marginBottom: '10px' }}>
|
||||
<CCol>
|
||||
<DatePicker
|
||||
selected={start === '' ? new Date() : new Date(start)}
|
||||
value={start === '' ? new Date() : new Date(start)}
|
||||
includeTime
|
||||
selectTime
|
||||
onChange={(date) => setStart(date)}
|
||||
/>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<DatePicker
|
||||
selected={end === '' ? new Date() : new Date(end)}
|
||||
value={end === '' ? new Date() : new Date(end)}
|
||||
includeTime
|
||||
selectTime
|
||||
onChange={(date) => setEnd(date)}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CCard>
|
||||
<div className="overflow-auto" style={{ height: '250px' }}>
|
||||
<CDataTable
|
||||
loading={loading}
|
||||
items={commands ?? []}
|
||||
fields={columns}
|
||||
style={{ color: 'white' }}
|
||||
border
|
||||
sorterValue={{ column: 'completed', desc: 'true' }}
|
||||
scopedSlots={{
|
||||
completed: (item) => <td>{cleanTimestamp(item.completed)}</td>,
|
||||
show_details: (item, index) => (
|
||||
<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.details, 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>
|
||||
}
|
||||
>
|
||||
<CIcon name="cilNotes" style={{ color: 'white' }} size="lg" />
|
||||
</CWidgetDropdown>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeviceCommands;
|
||||
@@ -1,6 +1,15 @@
|
||||
/* eslint-disable-rule prefer-destructuring */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { CWidgetProgress, CCollapse, CButton, CDataTable, CCard, CCardBody, CRow, CCol } from '@coreui/react';
|
||||
import {
|
||||
CWidgetProgress,
|
||||
CCollapse,
|
||||
CButton,
|
||||
CDataTable,
|
||||
CCard,
|
||||
CCardBody,
|
||||
CRow,
|
||||
CCol,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
@@ -11,6 +20,7 @@ import { getToken } from '../utils/authHelper';
|
||||
const DeviceHealth = () => {
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
const [details, setDetails] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [healthChecks, setHealthChecks] = useState([]);
|
||||
const [start, setStart] = useState(addDays(new Date(), -3).toString());
|
||||
const [end, setEnd] = useState(new Date().toString());
|
||||
@@ -24,6 +34,7 @@ const DeviceHealth = () => {
|
||||
};
|
||||
|
||||
const getDeviceHealth = () => {
|
||||
setLoading(true);
|
||||
const utcStart = new Date(start).toISOString();
|
||||
const utcEnd = new Date(end).toISOString();
|
||||
|
||||
@@ -32,10 +43,10 @@ const DeviceHealth = () => {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
},
|
||||
params : {
|
||||
params: {
|
||||
startDate: utcStart,
|
||||
endDate: utcEnd
|
||||
}
|
||||
endDate: utcEnd,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
@@ -46,6 +57,9 @@ const DeviceHealth = () => {
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
console.log(error.response);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -80,7 +94,7 @@ const DeviceHealth = () => {
|
||||
setStart(addDays(new Date(), -3).toString());
|
||||
setEnd(new Date().toString());
|
||||
}, []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
getDeviceHealth();
|
||||
}, [start, end]);
|
||||
@@ -102,15 +116,15 @@ const DeviceHealth = () => {
|
||||
footer={
|
||||
<div>
|
||||
<CCollapse show={collapse}>
|
||||
<CRow style={{ marginBottom: '10px' }}>
|
||||
<CRow style={{ marginBottom: '10px' }}>
|
||||
<CCol>
|
||||
<DatePicker
|
||||
selected={start === '' ? new Date() : new Date(start)}
|
||||
value={start === '' ? new Date() : new Date(start)}
|
||||
includeTime
|
||||
selectTime
|
||||
onChange={(date) => setStart(date)}
|
||||
/>
|
||||
selected={start === '' ? new Date() : new Date(start)}
|
||||
value={start === '' ? new Date() : new Date(start)}
|
||||
includeTime
|
||||
selectTime
|
||||
onChange={(date) => setStart(date)}
|
||||
/>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<DatePicker
|
||||
@@ -128,6 +142,8 @@ const DeviceHealth = () => {
|
||||
items={healthChecks ?? []}
|
||||
fields={columns}
|
||||
style={{ color: 'white' }}
|
||||
loading={loading}
|
||||
border
|
||||
sorterValue={{ column: 'recorded', desc: 'true' }}
|
||||
scopedSlots={{
|
||||
recorded: (item) => <td>{cleanTimestamp(item.recorded)}</td>,
|
||||
|
||||
@@ -187,7 +187,7 @@ const DeviceListDisplay = ({ devices, loading, updateDevicesPerPage, pageCount,
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CDataTable
|
||||
items={devices}
|
||||
items={devices ?? []}
|
||||
fields={columns}
|
||||
border
|
||||
hover
|
||||
|
||||
183
src/components/DeviceLogs.js
Normal file
183
src/components/DeviceLogs.js
Normal file
@@ -0,0 +1,183 @@
|
||||
/* eslint-disable-rule prefer-destructuring */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
CWidgetDropdown,
|
||||
CRow,
|
||||
CCol,
|
||||
CCollapse,
|
||||
CButton,
|
||||
CDataTable,
|
||||
CCard,
|
||||
CCardBody,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
import { cleanTimestamp, addDays } from '../utils/helper';
|
||||
import axiosInstance from '../utils/axiosInstance';
|
||||
import { getToken } from '../utils/authHelper';
|
||||
|
||||
const DeviceLogs = () => {
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
const [details, setDetails] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [logs, setLogs] = useState([]);
|
||||
const [start, setStart] = useState(addDays(new Date(), -3).toString());
|
||||
const [end, setEnd] = useState(new Date().toString());
|
||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
||||
|
||||
const toggle = (e) => {
|
||||
setCollapse(!collapse);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const getLogs = () => {
|
||||
setLoading(true);
|
||||
const utcStart = new Date(start).toISOString();
|
||||
const utcEnd = new Date(end).toISOString();
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
},
|
||||
params: {
|
||||
startDate: utcStart,
|
||||
endDate: utcEnd,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.get(`/device/${selectedDeviceId}/logs`, options)
|
||||
.then((response) => {
|
||||
setLogs(response.data.values);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
console.log(error.response);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
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 columns = [
|
||||
{ key: 'log' },
|
||||
{ key: 'severity' },
|
||||
{ key: 'recorded' },
|
||||
{
|
||||
key: 'show_details',
|
||||
label: '',
|
||||
_style: { width: '1%' },
|
||||
sorter: false,
|
||||
filter: false,
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
getLogs();
|
||||
setStart(addDays(new Date(), -3).toString());
|
||||
setEnd(new Date().toString());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getLogs();
|
||||
}, [start, end]);
|
||||
|
||||
return (
|
||||
<CWidgetDropdown
|
||||
inverse
|
||||
color="gradient-info"
|
||||
header="Device Logs"
|
||||
footerSlot={
|
||||
<div style={{ padding: '20px' }}>
|
||||
<CCollapse show={collapse}>
|
||||
<CRow style={{ marginBottom: '10px' }}>
|
||||
<CCol>
|
||||
<DatePicker
|
||||
selected={start === '' ? new Date() : new Date(start)}
|
||||
value={start === '' ? new Date() : new Date(start)}
|
||||
includeTime
|
||||
selectTime
|
||||
onChange={(date) => setStart(date)}
|
||||
/>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<DatePicker
|
||||
selected={end === '' ? new Date() : new Date(end)}
|
||||
value={end === '' ? new Date() : new Date(end)}
|
||||
includeTime
|
||||
selectTime
|
||||
onChange={(date) => setEnd(date)}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CCard>
|
||||
<div className="overflow-auto" style={{ height: '250px' }}>
|
||||
<CDataTable
|
||||
border
|
||||
items={logs ?? []}
|
||||
fields={columns}
|
||||
loading={loading}
|
||||
style={{ color: 'white' }}
|
||||
sorterValue={{ column: 'recorded', desc: 'true' }}
|
||||
scopedSlots={{
|
||||
recorded: (item) => <td>{cleanTimestamp(item.recorded)}</td>,
|
||||
show_details: (item, index) => (
|
||||
<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, 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>
|
||||
}
|
||||
>
|
||||
<CIcon name="cilList" style={{ color: 'white' }} size="lg" />
|
||||
</CWidgetDropdown>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeviceLogs;
|
||||
@@ -39,9 +39,9 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (chosenDate.trim() === ''){
|
||||
if (chosenDate.trim() === '') {
|
||||
setValidDate(false);
|
||||
valid = false
|
||||
valid = false;
|
||||
}
|
||||
return valid;
|
||||
};
|
||||
@@ -105,7 +105,7 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
|
||||
|
||||
const parameters = {
|
||||
serialNumber: selectedDeviceId,
|
||||
when: utcDateString,
|
||||
when: utcDateString,
|
||||
uri: firmware,
|
||||
};
|
||||
axiosInstance
|
||||
@@ -163,9 +163,7 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
|
||||
<CInvalidFeedback>You need a date...</CInvalidFeedback>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<div>
|
||||
Firmware URI:
|
||||
</div>
|
||||
<div>Firmware URI:</div>
|
||||
<CInput
|
||||
disabled={waiting}
|
||||
className={('form-control', { 'is-invalid': !validFirmware })}
|
||||
|
||||
@@ -34,4 +34,4 @@ export const addDays = (date, days) => {
|
||||
const newDate = new Date(date);
|
||||
newDate.setDate(date.getDate() + days);
|
||||
return newDate;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,6 +5,8 @@ import { CRow, CCol } from '@coreui/react';
|
||||
import DeviceHealth from '../../components/DeviceHealth';
|
||||
import DeviceConfiguration from '../../components/DeviceConfiguration';
|
||||
import DeviceActions from '../../components/DeviceActions';
|
||||
import DeviceCommands from '../../components/DeviceCommands';
|
||||
import DeviceLogs from '../../components/DeviceLogs';
|
||||
|
||||
const DevicePage = () => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -27,9 +29,11 @@ const DevicePage = () => {
|
||||
<div className="App">
|
||||
<CRow>
|
||||
<CCol xs="12" sm="6">
|
||||
<DeviceCommands />
|
||||
<DeviceConfiguration />
|
||||
</CCol>
|
||||
<CCol xs="12" sm="6">
|
||||
<DeviceLogs />
|
||||
<DeviceHealth />
|
||||
<DeviceActions />
|
||||
</CCol>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
CBadge,
|
||||
CCol,
|
||||
CRow,
|
||||
CInvalidFeedback
|
||||
CInvalidFeedback,
|
||||
} from '@coreui/react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
@@ -19,14 +19,7 @@ import 'react-widgets/styles.css';
|
||||
import { getToken } from '../utils/authHelper';
|
||||
import axiosInstance from '../utils/axiosInstance';
|
||||
|
||||
const ActionModalWidget = ({
|
||||
show,
|
||||
toggleModal,
|
||||
title,
|
||||
directions,
|
||||
action,
|
||||
extraParameters,
|
||||
}) => {
|
||||
const ActionModalWidget = ({ show, toggleModal, title, directions, action, extraParameters }) => {
|
||||
const [hadSuccess, setHadSuccess] = useState(false);
|
||||
const [hadFailure, setHadFailure] = useState(false);
|
||||
const [waiting, setWaiting] = useState(false);
|
||||
@@ -37,7 +30,7 @@ const ActionModalWidget = ({
|
||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
||||
|
||||
const formValidation = () => {
|
||||
if(chosenDate === ''){
|
||||
if (chosenDate === '') {
|
||||
setValidDate(false);
|
||||
return false;
|
||||
}
|
||||
@@ -89,15 +82,18 @@ const ActionModalWidget = ({
|
||||
const utcDate = new Date(chosenDate);
|
||||
const utcDateString = utcDate.toISOString();
|
||||
|
||||
const parameters = { ...{
|
||||
serialNumber: selectedDeviceId,
|
||||
when: utcDateString
|
||||
}, ...extraParameters}
|
||||
const parameters = {
|
||||
...{
|
||||
serialNumber: selectedDeviceId,
|
||||
when: utcDateString,
|
||||
},
|
||||
...extraParameters,
|
||||
};
|
||||
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.post(`/device/${selectedDeviceId}/${action}`, parameters, { headers })
|
||||
|
||||
Reference in New Issue
Block a user