mirror of
https://github.com/optim-enterprises-bv/OptimCloud-gw-ui.git
synced 2026-01-08 08:31:53 +00:00
User and Firmware tables now using modals
This commit is contained in:
18
package-lock.json
generated
18
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "2.3.4",
|
||||
"version": "2.3.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ucentral-client",
|
||||
"version": "2.3.4",
|
||||
"version": "2.3.5",
|
||||
"dependencies": {
|
||||
"@coreui/coreui": "^3.4.0",
|
||||
"@coreui/icons": "^2.0.1",
|
||||
@@ -32,7 +32,7 @@
|
||||
"react-tooltip": "^4.2.21",
|
||||
"react-widgets": "^5.1.1",
|
||||
"sass": "^1.35.1",
|
||||
"ucentral-libs": "^0.9.93",
|
||||
"ucentral-libs": "^0.9.94",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -14842,9 +14842,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ucentral-libs": {
|
||||
"version": "0.9.93",
|
||||
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.93.tgz",
|
||||
"integrity": "sha512-8DcG4TxSJEXBKHKpORTFcBFUpOvAGZLq1JJsYWbe10DXWMM0JRbahqkAmXHb1sNJock6hAhZeUtpXXI2MvBb/A==",
|
||||
"version": "0.9.94",
|
||||
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.94.tgz",
|
||||
"integrity": "sha512-sCvCdgxaj7aKcSC/T3+pPTAp3f09HSQQLeRsOAa/o3ZneoBIGVdhiOcKUR4sl/yP6Ew1ZH5J76PNQcm5Gu3fWA==",
|
||||
"dependencies": {
|
||||
"@coreui/coreui": "^3.4.0",
|
||||
"@coreui/icons": "^2.0.1",
|
||||
@@ -27716,9 +27716,9 @@
|
||||
}
|
||||
},
|
||||
"ucentral-libs": {
|
||||
"version": "0.9.93",
|
||||
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.93.tgz",
|
||||
"integrity": "sha512-8DcG4TxSJEXBKHKpORTFcBFUpOvAGZLq1JJsYWbe10DXWMM0JRbahqkAmXHb1sNJock6hAhZeUtpXXI2MvBb/A==",
|
||||
"version": "0.9.94",
|
||||
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.94.tgz",
|
||||
"integrity": "sha512-sCvCdgxaj7aKcSC/T3+pPTAp3f09HSQQLeRsOAa/o3ZneoBIGVdhiOcKUR4sl/yP6Ew1ZH5J76PNQcm5Gu3fWA==",
|
||||
"requires": {
|
||||
"@coreui/coreui": "^3.4.0",
|
||||
"@coreui/icons": "^2.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "2.3.4",
|
||||
"version": "2.3.5",
|
||||
"dependencies": {
|
||||
"@coreui/coreui": "^3.4.0",
|
||||
"@coreui/icons": "^2.0.1",
|
||||
@@ -26,7 +26,7 @@
|
||||
"react-tooltip": "^4.2.21",
|
||||
"react-widgets": "^5.1.1",
|
||||
"sass": "^1.35.1",
|
||||
"ucentral-libs": "^0.9.93",
|
||||
"ucentral-libs": "^0.9.94",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"main": "index.js",
|
||||
|
||||
@@ -14,11 +14,11 @@ import {
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
import { cilCloudDownload, cilSync, cilCalendarCheck } from '@coreui/icons';
|
||||
import { prettyDate, dateToUnix } from 'utils/helper';
|
||||
import { dateToUnix } from 'utils/helper';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import eventBus from 'utils/eventBus';
|
||||
import ConfirmModal from 'components/ConfirmModal';
|
||||
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs';
|
||||
import { LoadingButton, useAuth, useDevice, FormattedDate } from 'ucentral-libs';
|
||||
import WifiScanResultModalWidget from 'components/WifiScanResultModal';
|
||||
import DetailsModal from './DetailsModal';
|
||||
|
||||
@@ -273,25 +273,32 @@ const DeviceCommands = () => {
|
||||
className="text-white"
|
||||
sorterValue={{ column: 'created', desc: 'true' }}
|
||||
scopedSlots={{
|
||||
command: (item) => <td className="align-middle">{item.command}</td>,
|
||||
completed: (item) => (
|
||||
<td className="align-middle">
|
||||
{item.completed && item.completed !== 0
|
||||
? prettyDate(item.completed)
|
||||
: 'Pending'}
|
||||
{item.completed && item.completed !== 0 ? (
|
||||
<FormattedDate date={item.completed} />
|
||||
) : (
|
||||
'Pending'
|
||||
)}
|
||||
</td>
|
||||
),
|
||||
executed: (item) => (
|
||||
<td className="align-middle">
|
||||
{item.executed && item.executed !== 0
|
||||
? prettyDate(item.executed)
|
||||
: 'Pending'}
|
||||
{item.executed && item.executed !== 0 ? (
|
||||
<FormattedDate date={item.executed} />
|
||||
) : (
|
||||
'Pending'
|
||||
)}
|
||||
</td>
|
||||
),
|
||||
submitted: (item) => (
|
||||
<td className="align-middle">
|
||||
{item.submitted && item.submitted !== ''
|
||||
? prettyDate(item.submitted)
|
||||
: 'Pending'}
|
||||
{item.submitted && item.submitted !== '' ? (
|
||||
<FormattedDate date={item.submitted} />
|
||||
) : (
|
||||
'Pending'
|
||||
)}
|
||||
</td>
|
||||
),
|
||||
errorCode: (item) => <td className="align-middle">{item.errorCode}</td>,
|
||||
@@ -300,7 +307,7 @@ const DeviceCommands = () => {
|
||||
<CButtonToolbar
|
||||
role="group"
|
||||
className="justify-content-flex-end"
|
||||
style={{ width: '150px' }}
|
||||
style={{ width: '160px' }}
|
||||
>
|
||||
<CPopover
|
||||
content={
|
||||
|
||||
@@ -14,10 +14,10 @@ import CIcon from '@coreui/icons-react';
|
||||
import { cilTrash } from '@coreui/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
import { prettyDate, dateToUnix } from 'utils/helper';
|
||||
import { dateToUnix } from 'utils/helper';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import eventBus from 'utils/eventBus';
|
||||
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs';
|
||||
import { LoadingButton, useAuth, useDevice, FormattedDate } from 'ucentral-libs';
|
||||
import DeleteLogModal from 'components/DeleteLogModal';
|
||||
|
||||
const DeviceHealth = () => {
|
||||
@@ -190,7 +190,11 @@ const DeviceHealth = () => {
|
||||
sorterValue={{ column: 'recorded', desc: 'true' }}
|
||||
scopedSlots={{
|
||||
UUID: (item) => <td className="align-middle">{item.UUID}</td>,
|
||||
recorded: (item) => <td className="align-middle">{prettyDate(item.recorded)}</td>,
|
||||
recorded: (item) => (
|
||||
<td className="align-middle">
|
||||
<FormattedDate date={item.recorded} />
|
||||
</td>
|
||||
),
|
||||
sanity: (item) => <td className="align-middle">{`${item.sanity}%`}</td>,
|
||||
checkDetails: (item) => (
|
||||
<td>
|
||||
|
||||
@@ -15,10 +15,10 @@ import CIcon from '@coreui/icons-react';
|
||||
import { cilTrash } from '@coreui/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
import { prettyDate, dateToUnix } from 'utils/helper';
|
||||
import { dateToUnix } from 'utils/helper';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import eventBus from 'utils/eventBus';
|
||||
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs';
|
||||
import { LoadingButton, useAuth, useDevice, FormattedDate } from 'ucentral-libs';
|
||||
import DeleteLogModal from 'components/DeleteLogModal';
|
||||
|
||||
const DeviceLogs = () => {
|
||||
@@ -195,9 +195,16 @@ const DeviceLogs = () => {
|
||||
className="text-white"
|
||||
sorterValue={{ column: 'recorded', desc: 'true' }}
|
||||
scopedSlots={{
|
||||
recorded: (item) => <td>{prettyDate(item.recorded)}</td>,
|
||||
recorded: (item) => (
|
||||
<td className="align-middle">
|
||||
<FormattedDate date={item.recorded} />
|
||||
</td>
|
||||
),
|
||||
UUID: (item) => <td className="align-middle">{item.UUID}</td>,
|
||||
severity: (item) => <td className="align-middle">{item.severity}</td>,
|
||||
log: (item) => <td className="align-middle">{item.log}</td>,
|
||||
show_details: (item, index) => (
|
||||
<td className="py-2">
|
||||
<td className="align-middle">
|
||||
<CButton
|
||||
color="primary"
|
||||
variant={details.includes(index) ? '' : 'outline'}
|
||||
|
||||
230
src/components/EditFirmwareModal/index.js
Normal file
230
src/components/EditFirmwareModal/index.js
Normal file
@@ -0,0 +1,230 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CButton, CModal, CModalBody, CModalHeader, CModalTitle, CPopover } from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilPencil, cilSave, cilX } from '@coreui/icons';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import { useFormFields, useAuth, useToast, FirmwareDetailsForm } from 'ucentral-libs';
|
||||
|
||||
const initialState = {
|
||||
created: {
|
||||
value: '',
|
||||
error: false,
|
||||
editable: false,
|
||||
},
|
||||
release: {
|
||||
value: false,
|
||||
error: false,
|
||||
editable: false,
|
||||
},
|
||||
image: {
|
||||
value: '',
|
||||
error: false,
|
||||
editable: true,
|
||||
},
|
||||
imageDate: {
|
||||
value: '',
|
||||
error: false,
|
||||
editable: true,
|
||||
},
|
||||
size: {
|
||||
value: '',
|
||||
error: false,
|
||||
editable: true,
|
||||
},
|
||||
owner: {
|
||||
value: '',
|
||||
error: false,
|
||||
editable: true,
|
||||
},
|
||||
revision: {
|
||||
value: '',
|
||||
error: false,
|
||||
editable: false,
|
||||
},
|
||||
uri: {
|
||||
value: '',
|
||||
error: false,
|
||||
editable: true,
|
||||
},
|
||||
description: {
|
||||
value: '',
|
||||
error: false,
|
||||
editable: true,
|
||||
},
|
||||
notes: {
|
||||
value: [],
|
||||
editable: false,
|
||||
},
|
||||
};
|
||||
|
||||
const EditFirmwareModal = ({ show, toggle, firmwareId, refreshTable }) => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const { addToast } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [firmware, updateWithId, updateWithKey, setFirmware] = useFormFields(initialState);
|
||||
|
||||
const getFirmware = () => {
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${currentToken}`,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.get(`${endpoints.owfms}/api/v1/firmware/${firmwareId}`, options)
|
||||
.then((response) => {
|
||||
const newFirmware = {};
|
||||
|
||||
for (const key of Object.keys(response.data)) {
|
||||
if (key in initialState && key !== 'currentPassword') {
|
||||
newFirmware[key] = {
|
||||
...initialState[key],
|
||||
value: response.data[key],
|
||||
};
|
||||
}
|
||||
}
|
||||
setFirmware({ ...initialState, ...newFirmware });
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const toggleEditing = () => {
|
||||
if (editing) {
|
||||
getFirmware();
|
||||
}
|
||||
setEditing(!editing);
|
||||
};
|
||||
|
||||
const updateFirmware = () => {
|
||||
setLoading(true);
|
||||
|
||||
const parameters = {
|
||||
id: firmwareId,
|
||||
description: firmware.description.value,
|
||||
};
|
||||
|
||||
const newNotes = [];
|
||||
for (let i = 0; i < firmware.notes.value.length; i += 1) {
|
||||
if (firmware.notes.value[i].new) newNotes.push({ note: firmware.notes.value[i].note });
|
||||
}
|
||||
|
||||
parameters.notes = newNotes;
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${currentToken}`,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.put(`${endpoints.owfms}/api/v1/firmware/${firmwareId}`, parameters, options)
|
||||
.then(() => {
|
||||
addToast({
|
||||
title: t('firmware.update_success_title'),
|
||||
body: t('firmware.update_success'),
|
||||
color: 'success',
|
||||
autohide: true,
|
||||
});
|
||||
refreshTable();
|
||||
toggle();
|
||||
})
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('firmware.update_failure_title'),
|
||||
body: t('firmware.update_failure', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
getFirmware();
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const addNote = (currentNote) => {
|
||||
const newNotes = [...firmware.notes.value];
|
||||
newNotes.unshift({
|
||||
note: currentNote,
|
||||
new: true,
|
||||
created: new Date().getTime() / 1000,
|
||||
createdBy: '',
|
||||
});
|
||||
updateWithKey('notes', { value: newNotes });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
getFirmware();
|
||||
setEditing(false);
|
||||
}
|
||||
}, [show]);
|
||||
|
||||
return (
|
||||
<CModal show={show} onClose={toggle} size="xl">
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">
|
||||
{t('firmware.details_title', { image: firmware.image.value })}
|
||||
</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.save')}>
|
||||
<CButton color="primary" variant="outline" onClick={updateFirmware} disabled={loading}>
|
||||
<CIcon content={cilSave} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
<CPopover content={t('common.edit')}>
|
||||
<CButton
|
||||
disabled={editing}
|
||||
color="primary"
|
||||
variant="outline"
|
||||
onClick={toggleEditing}
|
||||
className="ml-2"
|
||||
>
|
||||
<CIcon name="cil-pencil" content={cilPencil} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
<CPopover content={t('common.stop_editing')}>
|
||||
<CButton
|
||||
disabled={!editing}
|
||||
color="primary"
|
||||
variant="outline"
|
||||
onClick={toggleEditing}
|
||||
className="ml-2"
|
||||
>
|
||||
<CIcon name="cil-x" content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<FirmwareDetailsForm
|
||||
t={t}
|
||||
fields={firmware}
|
||||
addNote={addNote}
|
||||
updateFieldsWithId={updateWithId}
|
||||
editing={editing}
|
||||
/>
|
||||
</CModalBody>
|
||||
</CModal>
|
||||
);
|
||||
};
|
||||
|
||||
EditFirmwareModal.propTypes = {
|
||||
firmwareId: PropTypes.string.isRequired,
|
||||
show: PropTypes.bool.isRequired,
|
||||
toggle: PropTypes.func.isRequired,
|
||||
refreshTable: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default React.memo(EditFirmwareModal);
|
||||
@@ -52,6 +52,7 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
|
||||
const { addToast } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [initialUser, setInitialUser] = useState({});
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [user, updateWithId, updateWithKey, setUser] = useUser(initialState);
|
||||
const [policies, setPolicies] = useState({
|
||||
passwordPolicy: '',
|
||||
@@ -98,6 +99,13 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const toggleEditing = () => {
|
||||
if (editing) {
|
||||
getUser();
|
||||
}
|
||||
setEditing(!editing);
|
||||
};
|
||||
|
||||
const updateUser = () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -197,6 +205,12 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
|
||||
}
|
||||
}, [userId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
setEditing(false);
|
||||
}
|
||||
}, [show]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
t={t}
|
||||
@@ -207,6 +221,8 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
|
||||
policies={policies}
|
||||
show={show}
|
||||
toggle={toggle}
|
||||
editing={editing}
|
||||
toggleEditing={toggleEditing}
|
||||
addNote={addNote}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import axiosInstance from 'utils/axiosInstance';
|
||||
import { CCard, CCardBody, CNav, CNavLink, CTabPane, CTabContent } from '@coreui/react';
|
||||
import { FirmwareList, useAuth, useToast } from 'ucentral-libs';
|
||||
import FirmwareDashboard from 'components/FirmwareDashboard';
|
||||
import EditFirmwareModal from 'components/EditFirmwareModal';
|
||||
|
||||
const FirmwareListPage = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -20,9 +21,14 @@ const FirmwareListPage = () => {
|
||||
const [filteredFirmware, setFilteredFirmware] = useState([]);
|
||||
const [displayedFirmware, setDisplayedFirmware] = useState([]);
|
||||
const [displayDev, setDisplayDev] = useState(false);
|
||||
const [addNoteLoading, setAddNoteLoading] = useState(false);
|
||||
const [updateDescriptionLoading, setUpdateDescriptionLoading] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [showEditModal, setShowEditModal] = useState(false);
|
||||
const [firmwareToEdit, setFirmwareToEdit] = useState('');
|
||||
|
||||
const toggleEditModal = (id) => {
|
||||
if (id) setFirmwareToEdit(id);
|
||||
setShowEditModal(!showEditModal);
|
||||
};
|
||||
|
||||
const displayFirmware = (currentPage, perPage, firmwareToDisplay) => {
|
||||
setLoading(true);
|
||||
@@ -149,70 +155,6 @@ const FirmwareListPage = () => {
|
||||
getFirmware(value);
|
||||
};
|
||||
|
||||
const addNote = (value, id) => {
|
||||
setAddNoteLoading(true);
|
||||
|
||||
const parameters = {
|
||||
id,
|
||||
notes: [{ note: value }],
|
||||
};
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${currentToken}`,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.put(`${endpoints.owfms}/api/v1/firmware/${id}`, parameters, options)
|
||||
.then(() => {
|
||||
getFirmware();
|
||||
setAddNoteLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setAddNoteLoading(false);
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('common.general_error'),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const updateDescription = (value, id) => {
|
||||
setUpdateDescriptionLoading(true);
|
||||
|
||||
const parameters = {
|
||||
id,
|
||||
description: value,
|
||||
};
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${currentToken}`,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.put(`${endpoints.owfms}/api/v1/firmware/${id}`, parameters, options)
|
||||
.then(() => {
|
||||
getFirmware();
|
||||
setUpdateDescriptionLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setUpdateDescriptionLoading(false);
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('common.general_error'),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedDeviceType === '' && !loading) getDeviceTypes();
|
||||
}, []);
|
||||
@@ -251,17 +193,20 @@ const FirmwareListPage = () => {
|
||||
setPage={updatePage}
|
||||
data={displayedFirmware}
|
||||
firmwarePerPage={firmwarePerPage}
|
||||
toggleEditModal={toggleEditModal}
|
||||
setFirmwarePerPage={updateFirmwarePerPage}
|
||||
selectedDeviceType={selectedDeviceType}
|
||||
deviceTypes={deviceTypes}
|
||||
setSelectedDeviceType={updateSelectedType}
|
||||
addNote={addNote}
|
||||
addNoteLoading={addNoteLoading}
|
||||
updateDescription={updateDescription}
|
||||
updateDescriptionLoading={updateDescriptionLoading}
|
||||
displayDev={displayDev}
|
||||
toggleDevDisplay={toggleDevDisplay}
|
||||
/>
|
||||
<EditFirmwareModal
|
||||
firmwareId={firmwareToEdit}
|
||||
show={showEditModal}
|
||||
toggle={toggleEditModal}
|
||||
refreshTable={getFirmware}
|
||||
/>
|
||||
</CTabPane>
|
||||
</CTabContent>
|
||||
</CCardBody>
|
||||
|
||||
Reference in New Issue
Block a user