diff --git a/package-lock.json b/package-lock.json index 3bb2dac..e7d0e41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ucentral-client", - "version": "2.5.20", + "version": "2.5.21", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ucentral-client", - "version": "2.5.20", + "version": "2.5.21", "dependencies": { "@coreui/coreui": "^3.4.0", "@coreui/icons": "^2.0.1", diff --git a/package.json b/package.json index 79b0708..617d23e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ucentral-client", - "version": "2.5.20", + "version": "2.5.21", "dependencies": { "@coreui/coreui": "^3.4.0", "@coreui/icons": "^2.0.1", diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index 68e3a4b..0243fef 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -118,6 +118,7 @@ "hours": "std", "id": "ID", "invalid_credentials": "Ungültiger Benutzername und / oder Passwort", + "invalid_date_explanation": "Ungültiges Datum, bitte verwenden Sie den Kalender, auf den Sie über die Schaltfläche rechts zugreifen können", "invalid_file": "Die ausgewählte Datei war ungültig, bitte lesen Sie die Anweisungen und passen Sie Ihre Datei entsprechend an", "invalid_password": "Dieses Passwort entspricht nicht den grundlegenden Passwortregeln. Bitte besuchen Sie unsere Seite Passwortrichtlinien, um mehr zu erfahren", "invalid_pem": "Ihre PEM-Datei ist ungültig. Es sollte mit '-----BEGIN CERTIFICATE-----' ODER '-----BEGIN PRIVATE KEY-----' beginnen und mit '-----END CERTIFICATE--- enden. --' ODER '-----END PRIVATSCHLÜSSEL-----'", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index ba8742e..e1c597e 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -118,6 +118,7 @@ "hours": "hours", "id": "Id", "invalid_credentials": "Invalid username and/or password", + "invalid_date_explanation": "Invalid Date, please use the calendar accessible with the button on the right ", "invalid_file": "The chosen file was invalid, please read the instructions and adjust your file accordingly", "invalid_password": "This password does not confirm to basic password rules. Please visit our Password Policy page to learn more", "invalid_pem": "Your .pem file is invalid. It should start with '-----BEGIN CERTIFICATE-----' OR '-----BEGIN PRIVATE KEY-----' and it should end with '-----END CERTIFICATE-----' OR '-----END PRIVATE KEY-----'", diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json index 3bfd0a0..7408d1a 100644 --- a/public/locales/es/translation.json +++ b/public/locales/es/translation.json @@ -118,6 +118,7 @@ "hours": "horas", "id": "Carné de identidad", "invalid_credentials": "Nombre de usuario y / o contraseña inválido", + "invalid_date_explanation": "Fecha no válida, utilice el calendario accesible con el botón de la derecha", "invalid_file": "El archivo elegido no es válido, lea las instrucciones y ajuste su archivo en consecuencia", "invalid_password": "Esta contraseña no confirma las reglas básicas de contraseña. Visite nuestra página de Política de contraseñas para obtener más información.", "invalid_pem": "Su archivo .pem no es válido. Debe comenzar con '----- BEGIN CERTIFICATE -----' O '----- BEGIN PRIVATE KEY -----' y debe terminar con '----- END CERTIFICATE --- - 'O' ----- FIN DE CLAVE PRIVADA ----- '", diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 01fb789..28fc9d8 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -118,6 +118,7 @@ "hours": "heures", "id": "Id", "invalid_credentials": "Nom d'utilisateur et / ou mot de passe incorrect", + "invalid_date_explanation": "Date invalide, merci d'utiliser le calendrier accessible avec le bouton à droite", "invalid_file": "Le fichier choisi n'était pas valide, veuillez lire les instructions et ajuster votre fichier en conséquence", "invalid_password": "Ce mot de passe ne confirme pas les règles de base des mots de passe. Veuillez visiter notre page Politique de mot de passe pour en savoir plus", "invalid_pem": "Votre fichier .pem n'est pas valide. Il doit commencer par '-----BEGIN CERTIFICATE-----' OU '-----BEGIN PRIVATE KEY-----' et il doit se terminer par '-----END CERTIFICATE--- --' OU '-----FIN CLÉ PRIVÉE-----'", diff --git a/public/locales/pt/translation.json b/public/locales/pt/translation.json index eae9a44..1fb9cb8 100644 --- a/public/locales/pt/translation.json +++ b/public/locales/pt/translation.json @@ -118,6 +118,7 @@ "hours": "horas", "id": "identidade", "invalid_credentials": "Nome de usuário e / ou senha inválidos", + "invalid_date_explanation": "Data inválida, use o calendário acessível com o botão à direita", "invalid_file": "O arquivo escolhido era inválido, por favor, leia as instruções e ajuste seu arquivo de acordo", "invalid_password": "Esta senha não está de acordo com as regras básicas de senha. Visite nossa página de Política de Senha para saber mais", "invalid_pem": "Seu arquivo .pem é inválido. Deve começar com '----- BEGIN CERTIFICATE -----' OU '----- BEGIN PRIVATE KEY -----' e deve terminar com '----- END CERTIFICATE --- - 'OU' ----- END PRIVATE KEY ----- '", diff --git a/src/components/CommandHistory/index.js b/src/components/CommandHistory/index.js index ccd8339..8c8647e 100644 --- a/src/components/CommandHistory/index.js +++ b/src/components/CommandHistory/index.js @@ -10,6 +10,7 @@ import { CCard, CPopover, CButtonToolbar, + CFormText, } from '@coreui/react'; import CIcon from '@coreui/icons-react'; import DatePicker from 'react-widgets/DatePicker'; @@ -41,7 +42,9 @@ const DeviceCommands = () => { const [commands, setCommands] = useState([]); const [loading, setLoading] = useState(false); const [start, setStart] = useState(''); + const [startError, setStartError] = useState(false); const [end, setEnd] = useState(''); + const [endError, setEndError] = useState(false); const [commandLimit, setCommandLimit] = useState(25); // Load more button related const [loadingMore, setLoadingMore] = useState(false); @@ -65,11 +68,25 @@ const DeviceCommands = () => { }; const modifyStart = (value) => { - setStart(value); + try { + new Date(value).toISOString(); + setStartError(false); + setStart(value); + } catch (e) { + setStart(''); + setStartError(true); + } }; const modifyEnd = (value) => { - setEnd(value); + try { + new Date(value).toISOString(); + setEndError(false); + setEnd(value); + } catch (e) { + setEnd(''); + setEndError(true); + } }; const deleteCommandFromList = (commandUuid) => { @@ -256,10 +273,16 @@ const DeviceCommands = () => { From: modifyStart(date)} /> + To: modifyEnd(date)} /> + diff --git a/src/components/InterfaceStatistics/StatisticsChartList.js b/src/components/InterfaceStatistics/StatisticsChartList.js index a30f993..9c291bd 100644 --- a/src/components/InterfaceStatistics/StatisticsChartList.js +++ b/src/components/InterfaceStatistics/StatisticsChartList.js @@ -4,11 +4,11 @@ import { useTranslation } from 'react-i18next'; import { v4 as createUuid } from 'uuid'; import axiosInstance from 'utils/axiosInstance'; import { useAuth, useDevice } from 'ucentral-libs'; -import { unixToTime, capitalizeFirstLetter } from 'utils/helper'; +import { capitalizeFirstLetter, dateToUnix, prettyDate } from 'utils/helper'; import eventBus from 'utils/eventBus'; import DeviceStatisticsChart from './DeviceStatisticsChart'; -const StatisticsChartList = ({ setOptions, section }) => { +const StatisticsChartList = ({ setOptions, section, start, end }) => { const { t } = useTranslation(); const [loading, setLoading] = useState(false); const { currentToken, endpoints } = useAuth(); @@ -52,7 +52,7 @@ const StatisticsChartList = ({ setOptions, section }) => { // Just building the array for all the interfaces for (const log of sortedData) { - categories.push(unixToTime(log.recorded)); + categories.push(prettyDate(log.recorded)); for (const logInterface of log.data.interfaces) { if (interfaceTypes[logInterface.name] === undefined) { interfaceTypes[logInterface.name] = i; @@ -93,7 +93,6 @@ const StatisticsChartList = ({ setOptions, section }) => { const interfaceOptions = { chart: { id: 'chart', - group: 'txrx', }, stroke: { curve: 'smooth', @@ -141,7 +140,6 @@ const StatisticsChartList = ({ setOptions, section }) => { }, }, categories, - tickAmount: 20, }, yaxis: { min: 0, @@ -174,7 +172,7 @@ const StatisticsChartList = ({ setOptions, section }) => { value: opt[0].titleName, label: opt[0].titleName, })); - setOptions([...sectionOptions, { value: 'memory', label: t('statistics.memory') }]); + setOptions([{ value: 'memory', label: t('statistics.memory') }, ...sectionOptions]); setStatOptions({ ...newOptions }); } }; @@ -217,11 +215,22 @@ const StatisticsChartList = ({ setOptions, section }) => { Accept: 'application/json', Authorization: `Bearer ${currentToken}`, }, + params: {}, }; + let extraParams = ''; + if (start !== '' && end !== '') { + const utcStart = new Date(start).toISOString(); + const utcEnd = new Date(end).toISOString(); + options.params.startDate = dateToUnix(utcStart); + options.params.endDate = dateToUnix(utcEnd); + } else { + extraParams = '?newest=true&limit=50'; + } + axiosInstance .get( - `${endpoints.owgw}/api/v1/device/${deviceSerialNumber}/statistics?newest=true&limit=50`, + `${endpoints.owgw}/api/v1/device/${deviceSerialNumber}/statistics${extraParams}`, options, ) .then((response) => { @@ -232,10 +241,10 @@ const StatisticsChartList = ({ setOptions, section }) => { }; useEffect(() => { - if (deviceSerialNumber) { + if (deviceSerialNumber && ((start !== '' && end !== '') || (start === '' && end === ''))) { getStatistics(); } - }, [deviceSerialNumber]); + }, [deviceSerialNumber, start, end]); useEffect(() => { eventBus.on('refreshInterfaceStatistics', () => getStatistics()); @@ -277,6 +286,8 @@ const StatisticsChartList = ({ setOptions, section }) => { StatisticsChartList.propTypes = { setOptions: PropTypes.func.isRequired, section: PropTypes.string.isRequired, + start: PropTypes.string.isRequired, + end: PropTypes.string.isRequired, }; export default React.memo(StatisticsChartList); diff --git a/src/components/InterfaceStatistics/index.js b/src/components/InterfaceStatistics/index.js index 25070ec..98c52e4 100644 --- a/src/components/InterfaceStatistics/index.js +++ b/src/components/InterfaceStatistics/index.js @@ -1,7 +1,16 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { v4 as createUuid } from 'uuid'; -import { CCard, CCardHeader, CCardBody, CPopover, CButton, CSelect } from '@coreui/react'; +import { + CCard, + CCardHeader, + CCardBody, + CPopover, + CButton, + CSelect, + CFormText, +} from '@coreui/react'; +import DatePicker from 'react-widgets/DatePicker'; import { cilSync } from '@coreui/icons'; import CIcon from '@coreui/icons-react'; import eventBus from 'utils/eventBus'; @@ -15,6 +24,10 @@ const DeviceStatisticsCard = () => { const [showLifetimeModal, setShowLifetimeModal] = useState(false); const [options, setOptions] = useState([]); const [section, setSection] = useState('memory'); + const [start, setStart] = useState(''); + const [startError, setStartError] = useState(false); + const [end, setEnd] = useState(''); + const [endError, setEndError] = useState(false); const toggleLatestModal = () => { setShowLatestModal(!showLatestModal); @@ -24,6 +37,28 @@ const DeviceStatisticsCard = () => { setShowLifetimeModal(!showLifetimeModal); }; + const modifyStart = (value) => { + try { + new Date(value).toISOString(); + setStartError(false); + setStart(value); + } catch (e) { + setStart(''); + setStartError(true); + } + }; + + const modifyEnd = (value) => { + try { + new Date(value).toISOString(); + setEndError(false); + setEnd(value); + } catch (e) { + setEnd(''); + setEndError(true); + } + }; + const refresh = () => { eventBus.dispatch('refreshInterfaceStatistics', { message: 'Refresh interface statistics' }); }; @@ -64,10 +99,24 @@ const DeviceStatisticsCard = () => { ))} +
+ modifyEnd(date)} /> + +
+ To: +
+ modifyStart(date)} /> + +
+ From: - +