Merge pull request #63 from stephb9959/dev

Version 2.3.9
This commit is contained in:
Charles
2021-10-28 14:24:50 -04:00
committed by GitHub
21 changed files with 396 additions and 179 deletions

18
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ucentral-client",
"version": "2.3.2",
"version": "2.3.9",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ucentral-client",
"version": "2.3.2",
"version": "2.3.9",
"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.89",
"ucentral-libs": "^0.9.98",
"uuid": "^8.3.2"
},
"devDependencies": {
@@ -14842,9 +14842,9 @@
}
},
"node_modules/ucentral-libs": {
"version": "0.9.89",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.89.tgz",
"integrity": "sha512-KH1RSQzN7AJTsryibxr3kfoYgr1Y80lQP5cB8mOcbqYamv3PS9F2QJHTQNoNOwQGASFd6D+WdnAj/viqV1wM0A==",
"version": "0.9.98",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.98.tgz",
"integrity": "sha512-Fwn0sqFS2u5bIN5gtbslgAZnki2J4iz6ucGVJmKdvghm+E45+KYFC2yGWGNlBfBkKIsSDlSIIhQHFEo0JTTCZg==",
"dependencies": {
"@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1",
@@ -27716,9 +27716,9 @@
}
},
"ucentral-libs": {
"version": "0.9.89",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.89.tgz",
"integrity": "sha512-KH1RSQzN7AJTsryibxr3kfoYgr1Y80lQP5cB8mOcbqYamv3PS9F2QJHTQNoNOwQGASFd6D+WdnAj/viqV1wM0A==",
"version": "0.9.98",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.98.tgz",
"integrity": "sha512-Fwn0sqFS2u5bIN5gtbslgAZnki2J4iz6ucGVJmKdvghm+E45+KYFC2yGWGNlBfBkKIsSDlSIIhQHFEo0JTTCZg==",
"requires": {
"@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "ucentral-client",
"version": "2.3.2",
"version": "2.3.9",
"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.89",
"ucentral-libs": "^0.9.98",
"uuid": "^8.3.2"
},
"main": "index.js",

View File

@@ -34,6 +34,8 @@
"access_policy": "Zugangsrichtlinien",
"add": "Hinzufügen",
"add_items": "Füge Artikel hinzu",
"add_note": "Notiz hinzufügen",
"add_note_explanation": "Schreiben Sie unten Ihre neue Notiz und klicken Sie auf die Schaltfläche \"+\", wo Sie fertig sind",
"adding_ellipsis": "Hinzufügen ...",
"are_you_sure": "Bist du sicher?",
"back_to_login": "Zurück zur Anmeldung",
@@ -89,6 +91,7 @@
"endpoints": "Endpunkte",
"error": "Fehler",
"error_adding_note": "Fehler beim Hinzufügen einer Notiz",
"error_code": "Fehlercode",
"execute_now": "Möchten Sie diesen Befehl jetzt ausführen?",
"executed": "Ausgeführt",
"exit": "Ausgang",

View File

@@ -34,6 +34,8 @@
"access_policy": "Access Policy",
"add": "Add",
"add_items": "Add Items",
"add_note": "Add Note",
"add_note_explanation": "Write your new note below and click the '+' button where you are done",
"adding_ellipsis": "Adding...",
"are_you_sure": "Are you sure?",
"back_to_login": "Back to Login",
@@ -89,6 +91,7 @@
"endpoints": "Endpoints",
"error": "Error",
"error_adding_note": "Error while adding note",
"error_code": "Error Code",
"execute_now": "Would you like to execute this command now?",
"executed": "Executed",
"exit": "Exit",

View File

@@ -34,6 +34,8 @@
"access_policy": "Política de acceso",
"add": "Añadir",
"add_items": "Agregar articulos",
"add_note": "Añadir la nota",
"add_note_explanation": "Escriba su nueva nota a continuación y haga clic en el botón '+' donde haya terminado",
"adding_ellipsis": "Añadiendo ...",
"are_you_sure": "¿Estás seguro?",
"back_to_login": "Atrás para iniciar sesión",
@@ -89,6 +91,7 @@
"endpoints": "Puntos finales",
"error": "Error",
"error_adding_note": "Error al agregar una nota",
"error_code": "código de error",
"execute_now": "¿Le gustaría ejecutar este comando ahora?",
"executed": "ejecutado",
"exit": "salida",

View File

@@ -34,6 +34,8 @@
"access_policy": "Politique d'accès",
"add": "Ajouter",
"add_items": "Ajouter des articles",
"add_note": "Ajouter une note",
"add_note_explanation": "Écrivez votre nouvelle note ci-dessous et cliquez sur le bouton '+' où vous avez terminé",
"adding_ellipsis": "Ajouter...",
"are_you_sure": "Êtes-vous sûr?",
"back_to_login": "Retour connexion",
@@ -89,6 +91,7 @@
"endpoints": "Points de terminaison",
"error": "Erreur",
"error_adding_note": "Erreur lors de l'ajout de la note",
"error_code": "Code d'erreur",
"execute_now": "Souhaitez-vous exécuter cette commande maintenant ?",
"executed": "réalisé",
"exit": "Sortie",

View File

@@ -34,6 +34,8 @@
"access_policy": "Política de Acesso",
"add": "Adicionar",
"add_items": "Adicionar itens",
"add_note": "Adicionar nota",
"add_note_explanation": "Escreva sua nova nota abaixo e clique no botão '+' quando terminar",
"adding_ellipsis": "Adicionando ...",
"are_you_sure": "Você tem certeza?",
"back_to_login": "Volte ao login",
@@ -89,6 +91,7 @@
"endpoints": "Pontos finais",
"error": "Erro",
"error_adding_note": "Erro ao adicionar nota",
"error_code": "Erro de código",
"execute_now": "Você gostaria de executar este comando agora?",
"executed": "Executado",
"exit": "Saída",

View File

@@ -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';
@@ -190,9 +190,9 @@ const DeviceCommands = () => {
const columns = [
{ key: 'submitted', label: t('common.submitted'), filter: false, _style: { width: '20%' } },
{ key: 'command', label: t('common.command'), _style: { width: '15%' } },
{ key: 'executed', label: t('common.executed'), filter: false, _style: { width: '20%' } },
{ key: 'completed', label: t('common.completed'), filter: false, _style: { width: '20%' } },
{ key: 'errorCode', label: t('common.code'), filter: false, _style: { width: '5%' } },
{ key: 'executed', label: t('common.executed'), filter: false, _style: { width: '16%' } },
{ key: 'completed', label: t('common.completed'), filter: false, _style: { width: '16%' } },
{ key: 'errorCode', label: t('common.error_code'), filter: false, _style: { width: '8%' } },
{
key: 'show_buttons',
label: '',
@@ -265,6 +265,7 @@ const DeviceCommands = () => {
<CCard>
<div className="overflow-auto" style={{ height: '200px' }}>
<CDataTable
addTableClasses="ignore-overflow table-sm"
border
loading={loading}
items={commands ?? []}
@@ -272,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>,
@@ -299,7 +307,7 @@ const DeviceCommands = () => {
<CButtonToolbar
role="group"
className="justify-content-flex-end"
style={{ width: '170px' }}
style={{ width: '160px' }}
>
<CPopover
content={
@@ -320,13 +328,13 @@ const DeviceCommands = () => {
<CIcon
name="cil-cloud-download"
content={cilCloudDownload}
size="lg"
size="md"
/>
) : (
<CIcon
name="cil-calendar-check"
content={cilCalendarCheck}
size="lg"
size="md"
/>
)}
</CButton>
@@ -342,7 +350,7 @@ const DeviceCommands = () => {
toggleResponse(item);
}}
>
<CIcon name="cilList" size="lg" />
<CIcon name="cilList" size="md" />
</CButton>
</CPopover>
<CPopover content={t('common.delete')}>
@@ -356,7 +364,7 @@ const DeviceCommands = () => {
toggleConfirmModal(item.UUID, index);
}}
>
<CIcon name="cilTrash" size="lg" />
<CIcon name="cilTrash" size="mdå" />
</CButton>
</CPopover>
</CButtonToolbar>

View File

@@ -20,11 +20,11 @@ const ConfigurationDisplay = ({ getData, deviceConfig }) => {
const { t } = useTranslation();
return (
<CCard className="m-0">
<CCardHeader className="p-1">
<CCardHeader className="dark-header">
<div className="d-flex flex-row-reverse align-items-center">
<div className="text-right">
<CPopover content={t('common.refresh')}>
<CButton size="sm" color="primary" variant="outline" onClick={getData}>
<CButton size="sm" color="info" onClick={getData}>
<CIcon content={cilSync} />
</CButton>
</CPopover>

View File

@@ -122,7 +122,7 @@ const DeviceActions = () => {
return (
<CCard>
<CCardHeader className="p-1">
<CCardHeader className="dark-header">
<div className="text-value-lg">{t('actions.title')}</div>
</CCardHeader>
<CCardBody>

View File

@@ -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 = () => {
@@ -181,6 +181,7 @@ const DeviceHealth = () => {
<CCard className="p-0">
<div className="overflow-auto" style={{ height: '200px' }}>
<CDataTable
addTableClasses="ignore-overflow table-sm"
border
items={healthChecks ?? []}
fields={columns}
@@ -189,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>

View File

@@ -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 = () => {
@@ -187,6 +187,7 @@ const DeviceLogs = () => {
<CCard>
<div className="overflow-auto" style={{ height: '250px' }}>
<CDataTable
addTableClasses="ignore-overflow table-sm"
border
items={logs ?? []}
fields={columns}
@@ -194,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'}
@@ -206,7 +214,7 @@ const DeviceLogs = () => {
toggleDetails(index);
}}
>
<CIcon name="cilList" size="lg" />
<CIcon name="cilList" size="md" />
</CButton>
</td>
),

View File

@@ -0,0 +1,224 @@
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 toggleModal = () => {
toggleEditing();
toggle();
};
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.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
<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);

View File

@@ -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}
/>
);

View File

@@ -28,22 +28,22 @@ const DeviceStatisticsCard = () => {
return (
<div>
<CCard className="m-0">
<CCardHeader className="p-1">
<CCardHeader className="dark-header">
<div className="d-flex flex-row-reverse align-items-center">
<div className="pl-2">
<CPopover content={t('common.refresh')}>
<CButton size="sm" color="primary" variant="outline" onClick={refresh}>
<CButton size="sm" color="info" onClick={refresh}>
<CIcon content={cilSync} />
</CButton>
</CPopover>
</div>
<div className="pl-2">
<CButton size="sm" color="primary" variant="outline" onClick={toggleLifetimeModal}>
<CButton size="sm" color="info" onClick={toggleLifetimeModal}>
Lifetime Statistics
</CButton>
</div>
<div className="pl-2">
<CButton size="sm" color="primary" variant="outline" onClick={toggleLatestModal}>
<CButton size="sm" color="info" onClick={toggleLatestModal}>
{t('statistics.show_latest')}
</CButton>
</div>

View File

@@ -201,13 +201,10 @@ const WifiAnalysis = () => {
return (
<div>
<CCard>
<CCardHeader>
<CCardHeader className="dark-header">
<CRow>
<CCol>
<h5 className="mb-0">{t('common.device', { serialNumber: deviceId })}</h5>
</CCol>
<CCol className="text-right">
<CButton color="primary" variant="outline" onClick={toggleModal}>
<CButton color="info" size="sm" onClick={toggleModal}>
{t('wifi_analysis.network_diagram')}
</CButton>
</CCol>

View File

@@ -24,6 +24,7 @@ const WifiChannelCard = ({ channel }) => {
<CCardBody>
<div className="overflow-auto" style={{ height: '250px' }}>
<CDataTable
addTableClasses="ignore-overflow table-sm"
items={channel.devices}
fields={columns}
className="text-white"

View File

@@ -18,7 +18,7 @@ const DeviceListPage = () => {
active={index === 0}
onClick={() => setIndex(0)}
>
{t('common.table')}
{t('common.dashboard')}
</CNavLink>
<CNavLink
className="font-weight-bold"
@@ -26,15 +26,15 @@ const DeviceListPage = () => {
active={index === 1}
onClick={() => setIndex(1)}
>
{t('common.dashboard')}
{t('common.devices')}
</CNavLink>
</CNav>
<CTabContent>
<CTabPane active={index === 0}>
<DeviceList />
<DeviceDashboard />
</CTabPane>
<CTabPane active={index === 1}>
<DeviceDashboard />
<DeviceList />
</CTabPane>
</CTabContent>
</CCardBody>

View File

@@ -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();
}, []);
@@ -227,7 +169,7 @@ const FirmwareListPage = () => {
active={index === 0}
onClick={() => setIndex(0)}
>
{t('common.table')}
{t('common.dashboard')}
</CNavLink>
<CNavLink
className="font-weight-bold"
@@ -235,11 +177,14 @@ const FirmwareListPage = () => {
active={index === 1}
onClick={() => setIndex(1)}
>
{t('common.dashboard')}
{t('common.firmware')}
</CNavLink>
</CNav>
<CTabContent>
<CTabPane active={index === 0}>
<FirmwareDashboard />
</CTabPane>
<CTabPane active={index === 1}>
<FirmwareList
t={t}
loading={loading}
@@ -248,20 +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}
/>
</CTabPane>
<CTabPane active={index === 1}>
<FirmwareDashboard />
<EditFirmwareModal
firmwareId={firmwareToEdit}
show={showEditModal}
toggle={toggleEditModal}
refreshTable={getFirmware}
/>
</CTabPane>
</CTabContent>
</CCardBody>

View File

@@ -63,6 +63,7 @@ const ProfilePage = () => {
const [userForm, updateWithId, updateWithKey, setUser] = useUser(initialState);
const [newAvatar, setNewAvatar] = useState('');
const [newAvatarFile, setNewAvatarFile] = useState(null);
const [avatarDeleted, setAvatarDeleted] = useState(false);
const [fileInputKey, setFileInputKey] = useState(0);
const [policies, setPolicies] = useState({
passwordPolicy: '',
@@ -161,8 +162,22 @@ const ProfilePage = () => {
const updateUser = () => {
setLoading(true);
if (newAvatarFile !== null) {
if (newAvatar !== '' && newAvatarFile !== null) {
uploadAvatar();
} else if (avatarDeleted) {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.delete(`${endpoints.owsec}/api/v1/avatar/${user.Id}`, options)
.then(() => {
getAvatar();
})
.catch(() => {});
}
if (
@@ -240,29 +255,15 @@ const ProfilePage = () => {
};
const showPreview = (e) => {
setAvatarDeleted(false);
const imageFile = e.target.files[0];
setNewAvatar(URL.createObjectURL(imageFile));
setNewAvatarFile(imageFile);
};
const deleteAvatar = () => {
setLoading(true);
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
return axiosInstance
.delete(`${endpoints.owsec}/api/v1/avatar/${user.Id}`, options)
.then(() => {
getAvatar();
})
.catch(() => {})
.finally(() => {
setLoading(false);
});
setNewAvatar('');
setAvatarDeleted(true);
};
const sendPhoneNumberTest = async (phoneNumber) => {
@@ -314,7 +315,12 @@ const ProfilePage = () => {
};
const toggleEditing = () => {
if (editing) getUser();
if (editing) {
setAvatarDeleted(false);
setNewAvatar('');
getUser();
getAvatar();
}
setEditing(!editing);
};
@@ -329,52 +335,30 @@ const ProfilePage = () => {
}, [user.Id]);
return (
<CCard>
<CCardHeader className="p-1">
<div className="text-value-lg float-left">{t('user.my_profile')}</div>
<CCard className="my-0 py-0">
<CCardHeader className="dark-header">
<div style={{ fontWeight: '600' }} className=" text-value-lg float-left">
{t('user.my_profile')}
</div>
<div className="text-right float-right">
<CButtonToolbar role="group" className="justify-content-end">
<CPopover content={t('common.save')}>
<CButton
disabled={!editing}
color="primary"
variant="outline"
onClick={updateUser}
className="mx-1"
>
<CButton disabled={!editing} color="info" onClick={updateUser} className="mx-1">
<CIcon name="cil-save" content={cilSave} />
</CButton>
</CPopover>
<CPopover content={t('common.edit')}>
<CButton
disabled={editing}
color="primary"
variant="outline"
onClick={toggleEditing}
className="mx-1"
>
<CButton disabled={editing} color="dark" onClick={toggleEditing} className="mx-1">
<CIcon name="cil-pencil" content={cilPencil} />
</CButton>
</CPopover>
<CPopover content={t('common.stop_editing')}>
<CButton
disabled={!editing}
color="primary"
variant="outline"
onClick={toggleEditing}
className="mx-1"
>
<CButton disabled={!editing} color="dark" onClick={toggleEditing} className="mx-1">
<CIcon name="cil-x" content={cilX} />
</CButton>
</CPopover>
<CPopover content={t('common.refresh')}>
<CButton
disabled={editing}
color="primary"
variant="outline"
onClick={getUser}
className="mx-1"
>
<CButton disabled={editing} color="info" onClick={getUser} className="mx-1">
<CIcon content={cilSync} />
</CButton>
</CPopover>
@@ -398,6 +382,7 @@ const ProfilePage = () => {
sendPhoneNumberTest={sendPhoneNumberTest}
testVerificationCode={testVerificationCode}
editing={editing}
avatarDeleted={avatarDeleted}
/>
</CCardBody>
</CCard>

View File

@@ -30,3 +30,16 @@ pre.ignore {
.tooltipRight { &::after { left: 75% !important; } }
.tooltipRight { &::before { left: 75% !important; } }
.dark-header {
padding: .25rem !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
color: #ebedef !important;
background-color: #2f3d54 !important;
}
.avatar-lg {
height: 10rem;
width: 10rem;
}