mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui.git
synced 2025-10-30 02:12:33 +00:00
UI fixes, standardization
This commit is contained in:
@@ -4,6 +4,7 @@ const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
|
||||
@@ -19,6 +20,9 @@ module.exports = {
|
||||
preferRelative: true,
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.VERSION': JSON.stringify(process.env.npm_package_version),
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: 'styles/[name].[contenthash].css',
|
||||
chunkFilename: '[id].[contenthash].css',
|
||||
|
||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "2.1.10",
|
||||
"version": "2.1.12",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ucentral-client",
|
||||
"version": "2.1.10",
|
||||
"version": "2.1.12",
|
||||
"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.17",
|
||||
"ucentral-libs": "^0.9.18",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -14812,9 +14812,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ucentral-libs": {
|
||||
"version": "0.9.17",
|
||||
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.17.tgz",
|
||||
"integrity": "sha512-GQtEaOhdFcBmNeu4NhYiq4yVsycZ7i8DeotubcKN8lt9v/TjAPecle0FsDdiXgXphOblToym6mk2tCAbH/958g==",
|
||||
"version": "0.9.18",
|
||||
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.18.tgz",
|
||||
"integrity": "sha512-znLLUdrRdBCYyufpFk/LWMVx0iajbuMTqayxWJek8JJeiaFlgH58+52s1OKjsLEb6Q4kPOIeUHguN3pJ/EfB1w==",
|
||||
"dependencies": {
|
||||
"@coreui/coreui": "^3.4.0",
|
||||
"@coreui/icons": "^2.0.1",
|
||||
@@ -27653,9 +27653,9 @@
|
||||
}
|
||||
},
|
||||
"ucentral-libs": {
|
||||
"version": "0.9.17",
|
||||
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.17.tgz",
|
||||
"integrity": "sha512-GQtEaOhdFcBmNeu4NhYiq4yVsycZ7i8DeotubcKN8lt9v/TjAPecle0FsDdiXgXphOblToym6mk2tCAbH/958g==",
|
||||
"version": "0.9.18",
|
||||
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.18.tgz",
|
||||
"integrity": "sha512-znLLUdrRdBCYyufpFk/LWMVx0iajbuMTqayxWJek8JJeiaFlgH58+52s1OKjsLEb6Q4kPOIeUHguN3pJ/EfB1w==",
|
||||
"requires": {
|
||||
"@coreui/coreui": "^3.4.0",
|
||||
"@coreui/icons": "^2.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "2.1.10",
|
||||
"version": "2.1.12",
|
||||
"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.17",
|
||||
"ucentral-libs": "^0.9.18",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"main": "index.js",
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
},
|
||||
"commands": {
|
||||
"error": "Fehler beim Senden des Befehls!",
|
||||
"error_delete_log": "Fehler beim Versuch zu löschen: {{error}}",
|
||||
"event_queue": "Ereigniswarteschlange",
|
||||
"success": "Befehl wurde erfolgreich übermittelt",
|
||||
"title": "Gerätebefehle",
|
||||
"unable_queue": "Anfrage für Ereigniswarteschlange kann nicht abgeschlossen werden"
|
||||
"unable_queue": "Anfrage für Ereigniswarteschlange kann nicht abgeschlossen werden: {{error}}"
|
||||
},
|
||||
"common": {
|
||||
"access_policy": "Zugangsrichtlinien",
|
||||
@@ -205,6 +206,9 @@
|
||||
"title": "Gerät konfigurieren",
|
||||
"valid_json": "Sie müssen ein gültiges JSON eingeben"
|
||||
},
|
||||
"connect": {
|
||||
"error_trying_to_connect": "Fehler beim Versuch, eine Verbindung zum Gerät herzustellen: {{error}}"
|
||||
},
|
||||
"delete_command": {
|
||||
"explanation": "Möchten Sie diesen Befehl wirklich löschen? Diese Aktion ist nicht umkehrbar.",
|
||||
"title": "Befehl löschen"
|
||||
@@ -215,6 +219,10 @@
|
||||
"explanation": "Dadurch werden alle {{object}} vor dem von Ihnen gewählten Datum gelöscht. Seien Sie vorsichtig, diese Aktion ist nicht umkehrbar.",
|
||||
"healthchecks_title": "Healthchecks löschen"
|
||||
},
|
||||
"device": {
|
||||
"error_fetching_device": "Fehler beim Abrufen der Geräteinformationen: {{error}}",
|
||||
"error_fetching_devices": "Fehler beim Abrufen von Geräten: {{error}}"
|
||||
},
|
||||
"device_logs": {
|
||||
"log": "Protokoll",
|
||||
"severity": "Wichtigkeit",
|
||||
@@ -486,7 +494,7 @@
|
||||
"create_success": "Benutzer erfolgreich erstellt",
|
||||
"creating": "Benutzer erstellen ...",
|
||||
"delete_avatar": "Avatar löschen",
|
||||
"delete_failure": "Fehler beim Versuch, den Benutzer zu löschen",
|
||||
"delete_failure": "Fehler beim Versuch, den Benutzer zu löschen: {{error}}",
|
||||
"delete_success": "Benutzer erfolgreich gelöscht!",
|
||||
"delete_title": "Benutzer löschen",
|
||||
"delete_warning": "Warnung: Sobald Sie einen Benutzer gelöscht haben, können Sie ihn nicht wiederherstellen",
|
||||
@@ -494,6 +502,7 @@
|
||||
"description": "Beschreibung",
|
||||
"edit": "Benutzer bearbeiten",
|
||||
"email_address": "E-Mail-Addresse",
|
||||
"error_fetching_users": "Fehler beim Abrufen der Nutzer: {{error}}",
|
||||
"force_password_change": "Passwortänderung bei der Anmeldung erzwingen",
|
||||
"id": "Benutzeridentifikation.",
|
||||
"last_login": "Letzte Anmeldung",
|
||||
@@ -509,7 +518,7 @@
|
||||
"provide_password": "Bitte geben Sie ein gültiges Passwort ein",
|
||||
"save_avatar": "Avatar speichern",
|
||||
"show_hide_password": "Passwort anzeigen/verbergen",
|
||||
"update_failure": "Stellen Sie sicher, dass alle Ihre Daten gültig sind. Wenn Sie das Kennwort ändern, stellen Sie sicher, dass es sich nicht um ein altes handelt.",
|
||||
"update_failure": "Fehler beim Aktualisieren: {{error}}",
|
||||
"update_failure_title": "Update fehlgeschlagen",
|
||||
"update_success": "Benutzer erfolgreich aktualisiert",
|
||||
"update_success_title": "Erfolg",
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
},
|
||||
"commands": {
|
||||
"error": "Error while submitting command!",
|
||||
"error_delete_log": "Error while trying to delete: {{error}}",
|
||||
"event_queue": "Event Queue",
|
||||
"success": "Command submitted successfully, you can look at the Commands log for the result",
|
||||
"title": "Command History",
|
||||
"unable_queue": "Unable to complete event queue request"
|
||||
"unable_queue": "Unable to complete event queue request: {{error}}"
|
||||
},
|
||||
"common": {
|
||||
"access_policy": "Access Policy",
|
||||
@@ -205,6 +206,9 @@
|
||||
"title": "Configure",
|
||||
"valid_json": "You need to enter valid JSON"
|
||||
},
|
||||
"connect": {
|
||||
"error_trying_to_connect": "Error while trying to connect to device: {{error}}"
|
||||
},
|
||||
"delete_command": {
|
||||
"explanation": "Are you sure you want to delete this command? This action is not reversible.",
|
||||
"title": "Delete Command"
|
||||
@@ -215,6 +219,10 @@
|
||||
"explanation": "This will delete all of the {{object}} before the date you choose. Be careful, this action is not reversible.",
|
||||
"healthchecks_title": "Delete Healthchecks"
|
||||
},
|
||||
"device": {
|
||||
"error_fetching_device": "Error fetching device information: {{error}}",
|
||||
"error_fetching_devices": "Error while fetching devices: {{error}}"
|
||||
},
|
||||
"device_logs": {
|
||||
"log": "Log",
|
||||
"severity": "Severity",
|
||||
@@ -486,7 +494,7 @@
|
||||
"create_success": "User Created Successfully",
|
||||
"creating": "Creating User...",
|
||||
"delete_avatar": "Delete Avatar",
|
||||
"delete_failure": "Error while trying to delete user",
|
||||
"delete_failure": "Error while trying to delete user: {{error}}",
|
||||
"delete_success": "User successfully deleted!",
|
||||
"delete_title": "Delete User",
|
||||
"delete_warning": "Warning: Once you delete a user you cannot revert",
|
||||
@@ -494,6 +502,7 @@
|
||||
"description": "Description",
|
||||
"edit": "Edit User",
|
||||
"email_address": "Email Address",
|
||||
"error_fetching_users": "Error fetching users: {{error}}",
|
||||
"force_password_change": "Force Password Change on Login",
|
||||
"id": "User Id.",
|
||||
"last_login": "Last Login",
|
||||
@@ -509,7 +518,7 @@
|
||||
"provide_password": "Please provide a valid password",
|
||||
"save_avatar": "Save Avatar",
|
||||
"show_hide_password": "Show/Hide Password",
|
||||
"update_failure": "Make sure all of your data is valid. If you are modifying the password, make sure it is not an old one.",
|
||||
"update_failure": "Error while trying to update: {{error}}",
|
||||
"update_failure_title": "Update Failed",
|
||||
"update_success": "User Updated Successfully",
|
||||
"update_success_title": "Success",
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
},
|
||||
"commands": {
|
||||
"error": "¡Error al enviar el comando!",
|
||||
"error_delete_log": "Error al intentar eliminar: {{error}}",
|
||||
"event_queue": "Cola de eventos",
|
||||
"success": "Comando enviado con éxito, puede consultar el registro de Comandos para ver el resultado",
|
||||
"title": "Historial de Comandos",
|
||||
"unable_queue": "No se pudo completar la solicitud de cola de eventos"
|
||||
"unable_queue": "No se pudo completar la solicitud de cola de eventos: {{error}}"
|
||||
},
|
||||
"common": {
|
||||
"access_policy": "Política de acceso",
|
||||
@@ -205,6 +206,9 @@
|
||||
"title": "Configurar",
|
||||
"valid_json": "Debes ingresar un JSON válido"
|
||||
},
|
||||
"connect": {
|
||||
"error_trying_to_connect": "Error al intentar conectarse al dispositivo: {{error}}"
|
||||
},
|
||||
"delete_command": {
|
||||
"explanation": "¿Está seguro de que desea eliminar este comando? Esta acción no es reversible.",
|
||||
"title": "Eliminar comando"
|
||||
@@ -215,6 +219,10 @@
|
||||
"explanation": "Esto eliminará todos los {{object}} antes de la fecha que elija. Tenga cuidado, esta acción no es reversible.",
|
||||
"healthchecks_title": "Eliminar comprobaciones de estado"
|
||||
},
|
||||
"device": {
|
||||
"error_fetching_device": "Error al obtener la información del dispositivo: {{error}}",
|
||||
"error_fetching_devices": "Error al recuperar dispositivos: {{error}}"
|
||||
},
|
||||
"device_logs": {
|
||||
"log": "Iniciar sesión",
|
||||
"severity": "Gravedad",
|
||||
@@ -486,7 +494,7 @@
|
||||
"create_success": "Usuario creado con éxito",
|
||||
"creating": "Creando usuario ...",
|
||||
"delete_avatar": "Eliminar avatar",
|
||||
"delete_failure": "Error al intentar eliminar al usuario",
|
||||
"delete_failure": "Error al intentar eliminar al usuario: {{error}}",
|
||||
"delete_success": "¡Usuario eliminado correctamente!",
|
||||
"delete_title": "Borrar usuario",
|
||||
"delete_warning": "Advertencia: una vez que elimina un usuario, no puede revertir",
|
||||
@@ -494,6 +502,7 @@
|
||||
"description": "Descripción",
|
||||
"edit": "editar usuario",
|
||||
"email_address": "Dirección de correo electrónico",
|
||||
"error_fetching_users": "Error al obtener usuarios: {{error}}",
|
||||
"force_password_change": "Forzar cambio de contraseña al iniciar sesión",
|
||||
"id": "Id. De usuario",
|
||||
"last_login": "Último acceso",
|
||||
@@ -509,7 +518,7 @@
|
||||
"provide_password": "Proporcione una contraseña válida",
|
||||
"save_avatar": "Guardar avatar",
|
||||
"show_hide_password": "Mostrar / Ocultar contraseña",
|
||||
"update_failure": "Asegúrese de que todos sus datos sean válidos. Si está modificando la contraseña, asegúrese de que no sea antigua.",
|
||||
"update_failure": "Error al intentar actualizar: {{error}}",
|
||||
"update_failure_title": "Actualización fallida",
|
||||
"update_success": "Usuario actualizado con éxito",
|
||||
"update_success_title": "Éxito",
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
},
|
||||
"commands": {
|
||||
"error": "Erreur lors de la soumission de la commande !",
|
||||
"error_delete_log": "Erreur lors de la tentative de suppression : {{error}}",
|
||||
"event_queue": "File d'attente d'événements",
|
||||
"success": "Commande soumise avec succès, vous pouvez consulter le journal des commandes pour le résultat",
|
||||
"title": "Historique des commandes",
|
||||
"unable_queue": "Impossible de terminer la demande de file d'attente d'événements"
|
||||
"unable_queue": "Impossible de terminer la demande de file d'attente d'événements: {{error}}"
|
||||
},
|
||||
"common": {
|
||||
"access_policy": "Politique d'accès",
|
||||
@@ -205,6 +206,9 @@
|
||||
"title": "Configurer",
|
||||
"valid_json": "Vous devez entrer un JSON valide"
|
||||
},
|
||||
"connect": {
|
||||
"error_trying_to_connect": "Erreur lors de la tentative de connexion à l'appareil : {{error}}"
|
||||
},
|
||||
"delete_command": {
|
||||
"explanation": "Êtes-vous sûr de vouloir supprimer cette commande ? Cette action n'est pas réversible.",
|
||||
"title": "Supprimer la commande"
|
||||
@@ -215,6 +219,10 @@
|
||||
"explanation": "Cela supprimera tous les {{object}} avant la date que vous choisissez. Attention, cette action n'est pas réversible.",
|
||||
"healthchecks_title": "Supprimer les vérifications d'état"
|
||||
},
|
||||
"device": {
|
||||
"error_fetching_device": "Erreur lors de la récupération des informations sur l'appareil : {{error}}",
|
||||
"error_fetching_devices": "Erreur lors de la récupération des appareils : {{error}}"
|
||||
},
|
||||
"device_logs": {
|
||||
"log": "Bûche",
|
||||
"severity": "Gravité",
|
||||
@@ -486,7 +494,7 @@
|
||||
"create_success": "L'utilisateur a été créé avec succès",
|
||||
"creating": "Création de l'utilisateur...",
|
||||
"delete_avatar": "Supprimer l'avatar",
|
||||
"delete_failure": "Erreur lors de la tentative de suppression de l'utilisateur",
|
||||
"delete_failure": "Erreur lors de la tentative de suppression de l'utilisateur: {{error}}",
|
||||
"delete_success": "Utilisateur supprimé avec succès !",
|
||||
"delete_title": "Supprimer l'utilisateur",
|
||||
"delete_warning": "Avertissement : Une fois que vous avez supprimé un utilisateur, vous ne pouvez plus revenir en arrière",
|
||||
@@ -494,6 +502,7 @@
|
||||
"description": "La description",
|
||||
"edit": "Modifier l'utilisateur",
|
||||
"email_address": "Adresse électronique",
|
||||
"error_fetching_users": "Erreur lors de la récupération des utilisateurs : {{error}}",
|
||||
"force_password_change": "Forcer le changement de mot de passe lors de la connexion",
|
||||
"id": "Identifiant d'utilisateur.",
|
||||
"last_login": "Dernière connexion",
|
||||
@@ -509,7 +518,7 @@
|
||||
"provide_password": "Veuillez fournir un mot de passe valide",
|
||||
"save_avatar": "Enregistrer l'avatar",
|
||||
"show_hide_password": "Afficher/Masquer le mot de passe",
|
||||
"update_failure": "Assurez-vous que toutes vos données sont valides. Si vous modifiez le mot de passe, assurez-vous qu'il ne s'agit pas d'un ancien.",
|
||||
"update_failure": "Erreur lors de la tentative de mise à jour : {{error}}",
|
||||
"update_failure_title": "mise à jour a échoué",
|
||||
"update_success": "L'utilisateur a bien été mis à jour",
|
||||
"update_success_title": "Succès",
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
},
|
||||
"commands": {
|
||||
"error": "Erro ao enviar comando!",
|
||||
"error_delete_log": "Erro ao tentar excluir: {{error}}",
|
||||
"event_queue": "Fila de Eventos",
|
||||
"success": "Comando enviado com sucesso, você pode consultar o log de Comandos para ver o resultado",
|
||||
"title": "Histórico de Comandos",
|
||||
"unable_queue": "Incapaz de completar o pedido de fila de eventos"
|
||||
"unable_queue": "Incapaz de completar o pedido de fila de eventos: {{error}}"
|
||||
},
|
||||
"common": {
|
||||
"access_policy": "Política de Acesso",
|
||||
@@ -205,6 +206,9 @@
|
||||
"title": "Configurar",
|
||||
"valid_json": "Você precisa inserir um JSON válido"
|
||||
},
|
||||
"connect": {
|
||||
"error_trying_to_connect": "Erro ao tentar conectar ao dispositivo: {{error}}"
|
||||
},
|
||||
"delete_command": {
|
||||
"explanation": "Tem certeza de que deseja excluir este comando? esta ação não é reversível.",
|
||||
"title": "Apagar Comando"
|
||||
@@ -215,6 +219,10 @@
|
||||
"explanation": "Isso excluirá todos os {{object}} antes da data que você escolheu. Cuidado, esta ação não é reversível.",
|
||||
"healthchecks_title": "Excluir verificações de saúde"
|
||||
},
|
||||
"device": {
|
||||
"error_fetching_device": "Erro ao buscar informações do dispositivo: {{error}}",
|
||||
"error_fetching_devices": "Erro ao buscar dispositivos: {{error}}"
|
||||
},
|
||||
"device_logs": {
|
||||
"log": "Registro",
|
||||
"severity": "Gravidade",
|
||||
@@ -486,7 +494,7 @@
|
||||
"create_success": "Usuário criado com sucesso",
|
||||
"creating": "Criando usuário ...",
|
||||
"delete_avatar": "Apagar Avatar",
|
||||
"delete_failure": "Erro ao tentar excluir usuário",
|
||||
"delete_failure": "Erro ao tentar excluir usuário: {{error}}",
|
||||
"delete_success": "Usuário excluído com sucesso!",
|
||||
"delete_title": "Deletar usuário",
|
||||
"delete_warning": "Aviso: depois de excluir um usuário, você não pode reverter",
|
||||
@@ -494,6 +502,7 @@
|
||||
"description": "Descrição",
|
||||
"edit": "Editar usuário",
|
||||
"email_address": "Endereço de e-mail",
|
||||
"error_fetching_users": "Erro ao buscar usuários: {{error}}",
|
||||
"force_password_change": "Forçar mudança de senha no login",
|
||||
"id": "ID do usuário.",
|
||||
"last_login": "Último login",
|
||||
@@ -509,7 +518,7 @@
|
||||
"provide_password": "Forneça uma senha válida",
|
||||
"save_avatar": "Salvar Avatar",
|
||||
"show_hide_password": "Mostrar / ocultar senha",
|
||||
"update_failure": "Certifique-se de que todos os seus dados são válidos. Se você estiver modificando a senha, certifique-se de que não seja uma senha antiga.",
|
||||
"update_failure": "Erro ao tentar atualizar: {{error}}",
|
||||
"update_failure_title": "Atualização falhou",
|
||||
"update_success": "Usuário atualizado com sucesso",
|
||||
"update_success_title": "Sucesso",
|
||||
|
||||
@@ -11,7 +11,10 @@ import {
|
||||
CFormGroup,
|
||||
CInputRadio,
|
||||
CLabel,
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
@@ -88,8 +91,15 @@ const BlinkModal = ({ show, toggleModal }) => {
|
||||
|
||||
return (
|
||||
<CModal show={show} onClose={toggleModal}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>{t('blink.device_leds')}</CModalTitle>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">{t('blink.device_leds')}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
{result === 'success' ? (
|
||||
<SuccessfulActionModalBody toggleModal={toggleModal} />
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
CButton,
|
||||
CModal,
|
||||
CModalHeader,
|
||||
CModalBody,
|
||||
CModalTitle,
|
||||
CModalFooter,
|
||||
} from '@coreui/react';
|
||||
import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
|
||||
const DetailsModal = ({ t, show, toggle, details, commandUuid }) => (
|
||||
<CModal size="lg" show={show} onClose={toggle}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="text-dark">{commandUuid}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<pre className="ignore">{JSON.stringify(details, null, 4)}</pre>
|
||||
</CModalBody>
|
||||
<CModalFooter>
|
||||
<CButton color="secondary" onClick={toggle}>
|
||||
{t('common.close')}
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
</CModal>
|
||||
);
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ import {
|
||||
CWidgetDropdown,
|
||||
CRow,
|
||||
CCol,
|
||||
CCollapse,
|
||||
CButton,
|
||||
CDataTable,
|
||||
CCard,
|
||||
CPopover,
|
||||
CButtonToolbar,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
@@ -21,7 +21,6 @@ import ConfirmModal from 'components/ConfirmModal';
|
||||
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs';
|
||||
import WifiScanResultModalWidget from 'components/WifiScanResultModal';
|
||||
import DetailsModal from './DetailsModal';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const DeviceCommands = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -38,8 +37,6 @@ const DeviceCommands = () => {
|
||||
const [showDetailsModal, setShowDetailsModal] = useState(false);
|
||||
const [detailsUuid, setDetailsUuid] = useState('');
|
||||
const [modalDetails, setModalDetails] = useState({});
|
||||
// Main collapsible
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
// General states
|
||||
const [commands, setCommands] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -50,11 +47,6 @@ const DeviceCommands = () => {
|
||||
const [loadingMore, setLoadingMore] = useState(false);
|
||||
const [showLoadingMore, setShowLoadingMore] = useState(true);
|
||||
|
||||
const toggle = (e) => {
|
||||
setCollapse(!collapse);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const toggleScanModal = () => {
|
||||
setShowScanModal(!showScanModal);
|
||||
};
|
||||
@@ -196,17 +188,15 @@ const DeviceCommands = () => {
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ key: 'UUID', label: t('common.id'), _style: { width: '28%' } },
|
||||
{ key: 'command', label: t('common.command'), _style: { width: '10%' } },
|
||||
{ key: 'completed', label: t('common.completed'), filter: false, _style: { width: '16%' } },
|
||||
{ key: 'submitted', label: t('common.submitted'), filter: false, _style: { width: '16%' } },
|
||||
{ key: 'executed', label: t('common.executed'), filter: false, _style: { width: '16%' } },
|
||||
{ key: 'command', label: t('common.command'), _style: { width: '15%' } },
|
||||
{ key: 'completed', label: t('common.completed'), filter: false, _style: { width: '20%' } },
|
||||
{ key: 'submitted', label: t('common.submitted'), filter: false, _style: { width: '20%' } },
|
||||
{
|
||||
key: 'show_buttons',
|
||||
label: '',
|
||||
sorter: false,
|
||||
filter: false,
|
||||
_style: { width: '14%' },
|
||||
_style: { width: '1%' },
|
||||
},
|
||||
];
|
||||
|
||||
@@ -252,23 +242,13 @@ const DeviceCommands = () => {
|
||||
}, [commands]);
|
||||
|
||||
return (
|
||||
<CWidgetDropdown
|
||||
inverse="true"
|
||||
color="gradient-primary"
|
||||
header={t('commands.title')}
|
||||
footerSlot={
|
||||
<div className={styles.footer}>
|
||||
<CCollapse show={collapse}>
|
||||
<CRow>
|
||||
<CCol />
|
||||
<CCol className="text-right">
|
||||
<div>
|
||||
<CButton onClick={refreshCommands} size="sm">
|
||||
<CIcon name="cil-sync" content={cilSync} className="text-white" size="2xl" />
|
||||
</CButton>
|
||||
</div>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<div>
|
||||
<CWidgetDropdown
|
||||
inverse="true"
|
||||
color="gradient-primary"
|
||||
header={t('commands.title')}
|
||||
footerSlot={
|
||||
<div className="pb-1 px-3">
|
||||
<CRow className="mb-2">
|
||||
<CCol>
|
||||
From:
|
||||
@@ -280,8 +260,9 @@ const DeviceCommands = () => {
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CCard>
|
||||
<div className={['overflow-auto', styles.scrollableBox].join(' ')}>
|
||||
<div className="overflow-auto" style={{ height: '200px' }}>
|
||||
<CDataTable
|
||||
border
|
||||
loading={loading}
|
||||
items={commands ?? []}
|
||||
fields={columns}
|
||||
@@ -302,98 +283,100 @@ const DeviceCommands = () => {
|
||||
: 'Pending'}
|
||||
</td>
|
||||
),
|
||||
executed: (item) => (
|
||||
<td>
|
||||
{item.executed && item.executed !== ''
|
||||
? prettyDate(item.executed)
|
||||
: 'Pending'}
|
||||
</td>
|
||||
),
|
||||
show_buttons: (item, index) => (
|
||||
<td>
|
||||
<CRow>
|
||||
<CCol>
|
||||
<CPopover
|
||||
content={
|
||||
item.command === 'trace' ? t('common.download') : t('common.result')
|
||||
}
|
||||
<CButtonToolbar
|
||||
role="group"
|
||||
className="justify-content-flex-end"
|
||||
style={{ width: '170px' }}
|
||||
>
|
||||
<CPopover
|
||||
content={
|
||||
item.command === 'trace' ? t('common.download') : t('common.result')
|
||||
}
|
||||
>
|
||||
<CButton
|
||||
color="primary"
|
||||
variant="outline"
|
||||
shape="square"
|
||||
size="sm"
|
||||
className="mx-2"
|
||||
onClick={() => {
|
||||
toggleDetails(item);
|
||||
}}
|
||||
>
|
||||
<CButton
|
||||
color="primary"
|
||||
variant="outline"
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDetails(item);
|
||||
}}
|
||||
>
|
||||
{item.command === 'trace' ? (
|
||||
<CIcon content={cilCloudDownload} size="lg" />
|
||||
) : (
|
||||
<CIcon content={cilCalendarCheck} size="lg" />
|
||||
)}
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CPopover content={t('common.details')}>
|
||||
<CButton
|
||||
color="primary"
|
||||
variant="outline"
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleResponse(item);
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilList" size="lg" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CPopover content={t('common.delete')}>
|
||||
<CButton
|
||||
color="primary"
|
||||
variant="outline"
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleConfirmModal(item.UUID, index);
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilTrash" size="lg" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</CCol>
|
||||
</CRow>
|
||||
{item.command === 'trace' ? (
|
||||
<CIcon
|
||||
name="cil-cloud-download"
|
||||
content={cilCloudDownload}
|
||||
size="lg"
|
||||
/>
|
||||
) : (
|
||||
<CIcon
|
||||
name="cil-calendar-check"
|
||||
content={cilCalendarCheck}
|
||||
size="lg"
|
||||
/>
|
||||
)}
|
||||
</CButton>
|
||||
</CPopover>
|
||||
<CPopover content={t('common.details')}>
|
||||
<CButton
|
||||
color="primary"
|
||||
variant="outline"
|
||||
shape="square"
|
||||
size="sm"
|
||||
className="mx-2"
|
||||
onClick={() => {
|
||||
toggleResponse(item);
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilList" size="lg" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
<CPopover content={t('common.delete')}>
|
||||
<CButton
|
||||
color="primary"
|
||||
variant="outline"
|
||||
shape="square"
|
||||
size="sm"
|
||||
className="mx-2"
|
||||
onClick={() => {
|
||||
toggleConfirmModal(item.UUID, index);
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilTrash" size="lg" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</CButtonToolbar>
|
||||
</td>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<CRow className={styles.loadMoreSpacing}>
|
||||
{showLoadingMore && (
|
||||
|
||||
{showLoadingMore && (
|
||||
<div className="mb-3">
|
||||
<LoadingButton
|
||||
label="View More"
|
||||
isLoadingLabel="Loading More..."
|
||||
label={t('common.view_more')}
|
||||
isLoadingLabel={t('common.loading_more_ellipsis')}
|
||||
isLoading={loadingMore}
|
||||
action={showMoreCommands}
|
||||
variant="outline"
|
||||
/>
|
||||
)}
|
||||
</CRow>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CCard>
|
||||
</CCollapse>
|
||||
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block>
|
||||
<CIcon
|
||||
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
||||
className="text-white"
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="text-right float-right">
|
||||
<CButton onClick={refreshCommands} size="sm">
|
||||
<CIcon name="cil-sync" content={cilSync} className="text-white" size="2xl" />
|
||||
</CButton>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
</CWidgetDropdown>
|
||||
|
||||
<WifiScanResultModalWidget
|
||||
show={showScanModal}
|
||||
toggle={toggleScanModal}
|
||||
@@ -408,7 +391,7 @@ const DeviceCommands = () => {
|
||||
details={modalDetails}
|
||||
commandUuid={detailsUuid}
|
||||
/>
|
||||
</CWidgetDropdown>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
.footer {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.scrollableBox {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.customIconHeight {
|
||||
height: 19px;
|
||||
}
|
||||
|
||||
.loadMoreSpacing {
|
||||
margin-bottom: 1%;
|
||||
}
|
||||
@@ -12,7 +12,10 @@ import {
|
||||
CTextarea,
|
||||
CInvalidFeedback,
|
||||
CInputFile,
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -125,8 +128,15 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
||||
|
||||
return (
|
||||
<CModal show={show} onClose={toggleModal} size="lg">
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>{t('configure.title')}</CModalTitle>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">{t('configure.title')}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
{hadSuccess ? (
|
||||
<SuccessfulActionModalBody toggleModal={toggleModal} />
|
||||
|
||||
@@ -9,7 +9,10 @@ import {
|
||||
CModalFooter,
|
||||
CSpinner,
|
||||
CBadge,
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const ConfirmModal = ({ show, toggle, action }) => {
|
||||
@@ -63,8 +66,15 @@ const ConfirmModal = ({ show, toggle, action }) => {
|
||||
|
||||
return (
|
||||
<CModal className="text-dark" show={show} onClose={toggle}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>{t('delete_command.title')}</CModalTitle>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">{t('delete_command.title')}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<h6>{t('delete_command.explanation')}</h6>
|
||||
@@ -73,9 +83,6 @@ const ConfirmModal = ({ show, toggle, action }) => {
|
||||
<CButton disabled={loading} color="primary" onClick={() => doAction()}>
|
||||
{getButtonContent()}
|
||||
</CButton>
|
||||
<CButton color="secondary" onClick={toggle}>
|
||||
{t('common.cancel')}
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
</CModal>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CModal, CModalHeader, CModalBody } from '@coreui/react';
|
||||
import { CModal, CModalHeader, CModalBody, CModalTitle, CPopover, CButton } from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilSave, cilX } from '@coreui/icons';
|
||||
import { CreateUserForm, useFormFields, useAuth, useToast } from 'ucentral-libs';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import { testRegex, validateEmail } from 'utils/helper';
|
||||
@@ -143,14 +145,26 @@ const CreateUserModal = ({ show, toggle, getUsers }) => {
|
||||
|
||||
return (
|
||||
<CModal show={show} onClose={toggle} size="xl">
|
||||
<CModalHeader>{t('user.create')}</CModalHeader>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">{t('user.create')}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('user.create')}>
|
||||
<CButton color="primary" variant="outline" onClick={createUser} disabled={loading}>
|
||||
<CIcon content={cilSave} />
|
||||
</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>
|
||||
<CreateUserForm
|
||||
t={t}
|
||||
fields={formFields}
|
||||
updateField={updateFieldWithId}
|
||||
createUser={createUser}
|
||||
loading={loading}
|
||||
policies={policies}
|
||||
toggleChange={toggleChange}
|
||||
/>
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CModal, CModalHeader, CModalTitle, CModalBody, CCol, CRow } from '@coreui/react';
|
||||
import {
|
||||
CModal,
|
||||
CModalHeader,
|
||||
CModalTitle,
|
||||
CModalBody,
|
||||
CCol,
|
||||
CRow,
|
||||
CPopover,
|
||||
CButton,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ConfirmFooter, useAuth, useDevice } from 'ucentral-libs';
|
||||
import { ConfirmFooter, useAuth, useDevice, useToast } from 'ucentral-libs';
|
||||
import { dateToUnix } from 'utils/helper';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import eventBus from 'utils/eventBus';
|
||||
@@ -11,6 +22,7 @@ import eventBus from 'utils/eventBus';
|
||||
const DeleteLogModal = ({ show, toggle, object }) => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const { addToast } = useToast();
|
||||
const { deviceSerialNumber } = useDevice();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [maxDate, setMaxDate] = useState(new Date().toString());
|
||||
@@ -36,7 +48,14 @@ const DeleteLogModal = ({ show, toggle, object }) => {
|
||||
return axiosInstance
|
||||
.delete(`${endpoints.owgw}/api/v1/device/${deviceSerialNumber}/${object}`, options)
|
||||
.then(() => {})
|
||||
.catch(() => {})
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('commands.error_delete_log', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
if (object === 'healthchecks')
|
||||
eventBus.dispatch('deletedHealth', { message: 'Healthcheck was deleted' });
|
||||
@@ -54,12 +73,19 @@ const DeleteLogModal = ({ show, toggle, object }) => {
|
||||
|
||||
return (
|
||||
<CModal className="text-dark" show={show} onClose={toggle}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">
|
||||
{object === 'healthchecks'
|
||||
? t('delete_logs.healthchecks_title')
|
||||
: t('delete_logs.device_logs_title')}
|
||||
</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<h6>{t('delete_logs.explanation', { object })}</h6>
|
||||
|
||||
@@ -70,7 +70,14 @@ const DeviceActions = () => {
|
||||
const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
|
||||
if (newWindow) newWindow.opener = null;
|
||||
})
|
||||
.catch(() => {})
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('connect.error_trying_to_connect', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setConnectLoading(false);
|
||||
});
|
||||
@@ -131,7 +138,7 @@ const DeviceActions = () => {
|
||||
</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-3">
|
||||
<CRow className="mt-4">
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={toggleUpgradeModal}>
|
||||
{t('actions.firmware_upgrade')}
|
||||
@@ -143,7 +150,7 @@ const DeviceActions = () => {
|
||||
</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-3">
|
||||
<CRow className="mt-4">
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={toggleScanModal}>
|
||||
{t('actions.wifi_scan')}
|
||||
@@ -155,7 +162,7 @@ const DeviceActions = () => {
|
||||
</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-3">
|
||||
<CRow className="mt-4">
|
||||
<CCol>
|
||||
<LoadingButton
|
||||
isLoading={connectLoading}
|
||||
@@ -170,7 +177,7 @@ const DeviceActions = () => {
|
||||
</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-3">
|
||||
<CRow className="mt-4">
|
||||
<CCol>
|
||||
<CButton block color="primary" onClick={toggleQueueModal}>
|
||||
{t('commands.event_queue')}
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
NotesTable,
|
||||
useAuth,
|
||||
useDevice,
|
||||
useToast,
|
||||
} from 'ucentral-libs';
|
||||
import DeviceConfigurationModal from './DeviceConfigurationModal';
|
||||
|
||||
@@ -29,6 +30,7 @@ const DeviceConfiguration = () => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const { deviceSerialNumber } = useDevice();
|
||||
const { addToast } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
@@ -61,7 +63,14 @@ const DeviceConfiguration = () => {
|
||||
.then((response) => {
|
||||
setDevice(response.data);
|
||||
})
|
||||
.catch(() => {});
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('device.error_fetching_device', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const saveNote = (currentNote) => {
|
||||
@@ -153,46 +162,46 @@ const DeviceConfiguration = () => {
|
||||
{device.firmware}
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-2">
|
||||
<CCol md="3">
|
||||
<CLabel>{t('configuration.last_configuration_change')} : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{prettyDate(device.lastConfigurationChange)}
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-2">
|
||||
<CCol md="3">
|
||||
<CLabel>{t('common.mac')} :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.macAddress}
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-2 mb-4">
|
||||
<CCol md="3">
|
||||
<CLabel className="align-middle">{t('configuration.device_password')} : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="2">
|
||||
{getPassword()}
|
||||
</CCol>
|
||||
<CCol md="7">
|
||||
<HideTextButton t={t} toggle={toggleShowPassword} show={showPassword} />
|
||||
<CopyToClipboardButton
|
||||
t={t}
|
||||
size="sm"
|
||||
content={device?.devicePassword === '' ? 'openwifi' : device.devicePassword}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<NotesTable
|
||||
t={t}
|
||||
notes={device.notes}
|
||||
loading={loading}
|
||||
addNote={saveNote}
|
||||
descriptionColumn={false}
|
||||
/>
|
||||
<CCollapse show={collapse}>
|
||||
<CRow className="mt-2">
|
||||
<CCol md="3">
|
||||
<CLabel>{t('configuration.last_configuration_change')} : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{prettyDate(device.lastConfigurationChange)}
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-2">
|
||||
<CCol md="3">
|
||||
<CLabel>{t('common.mac')} :</CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="9">
|
||||
{device.macAddress}
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-2 mb-4">
|
||||
<CCol md="3">
|
||||
<CLabel className="align-middle">{t('configuration.device_password')} : </CLabel>
|
||||
</CCol>
|
||||
<CCol xs="12" md="2">
|
||||
{getPassword()}
|
||||
</CCol>
|
||||
<CCol md="7">
|
||||
<HideTextButton t={t} toggle={toggleShowPassword} show={showPassword} />
|
||||
<CopyToClipboardButton
|
||||
t={t}
|
||||
size="sm"
|
||||
content={device?.devicePassword === '' ? 'openwifi' : device.devicePassword}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<NotesTable
|
||||
t={t}
|
||||
notes={device.notes}
|
||||
loading={loading}
|
||||
addNote={saveNote}
|
||||
descriptionColumn={false}
|
||||
/>
|
||||
<CRow className="mt-2">
|
||||
<CCol md="3">
|
||||
<CLabel>{t('configuration.last_configuration_download')} : </CLabel>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
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';
|
||||
@@ -25,7 +26,6 @@ const DeviceHealth = () => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const { deviceSerialNumber } = useDevice();
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
const [details, setDetails] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [healthChecks, setHealthChecks] = useState([]);
|
||||
@@ -42,11 +42,6 @@ const DeviceHealth = () => {
|
||||
setShowDeleteModal(!showDeleteModal);
|
||||
};
|
||||
|
||||
const toggle = (e) => {
|
||||
setCollapse(!collapse);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const modifyStart = (value) => {
|
||||
setStart(value);
|
||||
};
|
||||
@@ -195,96 +190,71 @@ const DeviceHealth = () => {
|
||||
color={barColor}
|
||||
inverse="true"
|
||||
footerSlot={
|
||||
<div className="p-4">
|
||||
<div className="pb-1 px-3">
|
||||
<CProgress className="mb-3" color="white" value={sanityLevel ?? 0} />
|
||||
<CCollapse show={collapse}>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.delete')}>
|
||||
<CButton
|
||||
color="light"
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDeleteModal();
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilTrash" size="lg" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
<CRow className="mb-3">
|
||||
<CCol>
|
||||
{t('common.from')}
|
||||
:
|
||||
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
||||
</CCol>
|
||||
<CCol>
|
||||
{t('common.to')}
|
||||
:
|
||||
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CCard className="p-0">
|
||||
<div className="overflow-auto" style={{ height: '200px' }}>
|
||||
<CDataTable
|
||||
border
|
||||
items={healthChecks ?? []}
|
||||
fields={columns}
|
||||
className="text-white"
|
||||
loading={loading}
|
||||
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>,
|
||||
sanity: (item) => <td className="align-middle">{`${item.sanity}%`}</td>,
|
||||
show_details: (item, index) => (
|
||||
<td className="align-middle">
|
||||
<CButton
|
||||
color="primary"
|
||||
variant={details.includes(index) ? '' : 'outline'}
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDetails(index);
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilList" size="lg" />
|
||||
</CButton>
|
||||
</td>
|
||||
),
|
||||
details: (item, index) => (
|
||||
<CCollapse show={details.includes(index)}>
|
||||
<CCardBody>
|
||||
<h5>{t('common.details')}</h5>
|
||||
<div>{getDetails(index, item.values)}</div>
|
||||
</CCardBody>
|
||||
</CCollapse>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{showLoadingMore && (
|
||||
<div className="mb-3">
|
||||
<LoadingButton
|
||||
label={t('common.view_more')}
|
||||
isLoadingLabel={t('common.loading_more_ellipsis')}
|
||||
isLoading={loadingMore}
|
||||
action={showMoreLogs}
|
||||
variant="outline"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<CRow className="mb-3">
|
||||
<CCol>
|
||||
{t('common.from')}
|
||||
:
|
||||
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
||||
</CCol>
|
||||
<CCol>
|
||||
{t('common.to')}
|
||||
:
|
||||
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CCard className="p-0">
|
||||
<div className="overflow-auto" style={{ height: '250px' }}>
|
||||
<CDataTable
|
||||
border
|
||||
items={healthChecks ?? []}
|
||||
fields={columns}
|
||||
className="text-white"
|
||||
loading={loading}
|
||||
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>
|
||||
),
|
||||
sanity: (item) => <td className="align-middle">{`${item.sanity}%`}</td>,
|
||||
show_details: (item, index) => (
|
||||
<td className="align-middle">
|
||||
<CButton
|
||||
color="primary"
|
||||
variant={details.includes(index) ? '' : 'outline'}
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDetails(index);
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilList" size="lg" />
|
||||
</CButton>
|
||||
</td>
|
||||
),
|
||||
details: (item, index) => (
|
||||
<CCollapse show={details.includes(index)}>
|
||||
<CCardBody>
|
||||
<h5>{t('common.details')}</h5>
|
||||
<div>{getDetails(index, item.values)}</div>
|
||||
</CCardBody>
|
||||
</CCollapse>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{showLoadingMore && (
|
||||
<div className="mb-3">
|
||||
<LoadingButton
|
||||
label={t('common.view_more')}
|
||||
isLoadingLabel={t('common.loading_more_ellipsis')}
|
||||
isLoading={loadingMore}
|
||||
action={showMoreLogs}
|
||||
variant="outline"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CCard>
|
||||
</CCollapse>
|
||||
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block>
|
||||
<CIcon
|
||||
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
||||
className="text-white"
|
||||
size="lg"
|
||||
/>
|
||||
</CButton>
|
||||
</CCard>
|
||||
<DeleteLogModal
|
||||
serialNumber={deviceSerialNumber}
|
||||
object="healthchecks"
|
||||
@@ -293,7 +263,15 @@ const DeviceHealth = () => {
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
>
|
||||
<div className="text-right float-right">
|
||||
<CPopover content={t('common.delete')}>
|
||||
<CButton onClick={toggleDeleteModal} size="sm">
|
||||
<CIcon name="cil-trash" content={cilTrash} className="text-white" size="2xl" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CWidgetDropdown>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -99,7 +99,13 @@ const DeviceList = () => {
|
||||
setDevices(fullDevices);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('device.error_fetching_devices', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
@@ -130,7 +136,13 @@ const DeviceList = () => {
|
||||
}
|
||||
getDeviceInformation(selectedPage);
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('device.error_fetching_devices', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
@@ -179,7 +191,13 @@ const DeviceList = () => {
|
||||
setDevices(newList);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('device.error_fetching_devices', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
@@ -282,10 +300,10 @@ const DeviceList = () => {
|
||||
const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
|
||||
if (newWindow) newWindow.opener = null;
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('common.unable_to_connect'),
|
||||
body: t('connect.error_trying_to_connect', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
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';
|
||||
@@ -24,7 +25,6 @@ const DeviceLogs = () => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const { deviceSerialNumber } = useDevice();
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
const [details, setDetails] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [logs, setLogs] = useState([]);
|
||||
@@ -39,11 +39,6 @@ const DeviceLogs = () => {
|
||||
setShowDeleteModal(!showDeleteModal);
|
||||
};
|
||||
|
||||
const toggle = (e) => {
|
||||
setCollapse(!collapse);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const modifyStart = (value) => {
|
||||
setStart(value);
|
||||
};
|
||||
@@ -176,91 +171,76 @@ const DeviceLogs = () => {
|
||||
color="gradient-info"
|
||||
header={t('device_logs.title')}
|
||||
footerSlot={
|
||||
<div className="p-4">
|
||||
<CCollapse show={collapse}>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.delete')}>
|
||||
<CButton
|
||||
color="light"
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDeleteModal();
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilTrash" size="lg" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
<div className="pb-1 px-3">
|
||||
<CRow className="mb-3">
|
||||
<CCol>
|
||||
{t('common.from')}
|
||||
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
||||
</CCol>
|
||||
<CCol>
|
||||
{t('common.to')}
|
||||
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CCard>
|
||||
<div className="overflow-auto" style={{ height: '250px' }}>
|
||||
<CDataTable
|
||||
items={logs ?? []}
|
||||
fields={columns}
|
||||
loading={loading}
|
||||
className="text-white"
|
||||
sorterValue={{ column: 'recorded', desc: 'true' }}
|
||||
scopedSlots={{
|
||||
recorded: (item) => <td>{prettyDate(item.recorded)}</td>,
|
||||
show_details: (item, index) => (
|
||||
<td className="py-2">
|
||||
<CButton
|
||||
color="primary"
|
||||
variant={details.includes(index) ? '' : 'outline'}
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDetails(index);
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilList" size="lg" />
|
||||
</CButton>
|
||||
</td>
|
||||
),
|
||||
details: (item, index) => (
|
||||
<CCollapse show={details.includes(index)}>
|
||||
<CCardBody>
|
||||
<h5>{t('common.details')}</h5>
|
||||
<div>{getDetails(index, item)}</div>
|
||||
</CCardBody>
|
||||
</CCollapse>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{showLoadingMore && (
|
||||
<div className="mb-3">
|
||||
<LoadingButton
|
||||
label={t('common.view_more')}
|
||||
isLoadingLabel={t('common.loading_more_ellipsis')}
|
||||
isLoading={loadingMore}
|
||||
action={showMoreLogs}
|
||||
variant="outline"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<CRow className="mb-3">
|
||||
<CCol>
|
||||
{t('common.from')}
|
||||
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
||||
</CCol>
|
||||
<CCol>
|
||||
{t('common.to')}
|
||||
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CCard>
|
||||
<div className="overflow-auto" style={{ height: '250px' }}>
|
||||
<CDataTable
|
||||
items={logs ?? []}
|
||||
fields={columns}
|
||||
loading={loading}
|
||||
className="text-white"
|
||||
sorterValue={{ column: 'recorded', desc: 'true' }}
|
||||
scopedSlots={{
|
||||
recorded: (item) => <td>{prettyDate(item.recorded)}</td>,
|
||||
show_details: (item, index) => (
|
||||
<td className="py-2">
|
||||
<CButton
|
||||
color="primary"
|
||||
variant={details.includes(index) ? '' : 'outline'}
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDetails(index);
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilList" size="lg" />
|
||||
</CButton>
|
||||
</td>
|
||||
),
|
||||
details: (item, index) => (
|
||||
<CCollapse show={details.includes(index)}>
|
||||
<CCardBody>
|
||||
<h5>{t('common.details')}</h5>
|
||||
<div>{getDetails(index, item)}</div>
|
||||
</CCardBody>
|
||||
</CCollapse>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{showLoadingMore && (
|
||||
<div className="mb-3">
|
||||
<LoadingButton
|
||||
label={t('common.view_more')}
|
||||
isLoadingLabel={t('common.loading_more_ellipsis')}
|
||||
isLoading={loadingMore}
|
||||
action={showMoreLogs}
|
||||
variant="outline"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CCard>
|
||||
</CCollapse>
|
||||
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block>
|
||||
<CIcon
|
||||
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
||||
className="text-white"
|
||||
size="lg"
|
||||
/>
|
||||
</CButton>
|
||||
</CCard>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
>
|
||||
<div className="text-right float-right">
|
||||
<CPopover content={t('common.delete')}>
|
||||
<CButton onClick={toggleDeleteModal} size="sm">
|
||||
<CIcon name="cil-trash" content={cilTrash} className="text-white" size="2xl" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CWidgetDropdown>
|
||||
<DeleteLogModal
|
||||
serialNumber={deviceSerialNumber}
|
||||
object="logs"
|
||||
|
||||
@@ -1,17 +1,42 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import { DeviceStatusCard as Card, useDevice, useAuth } from 'ucentral-libs';
|
||||
import { DeviceStatusCard as Card, useDevice, useAuth, useToast } from 'ucentral-libs';
|
||||
|
||||
const DeviceStatusCard = () => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const { deviceSerialNumber } = useDevice();
|
||||
const { addToast } = useToast();
|
||||
const [lastStats, setLastStats] = useState(null);
|
||||
const [status, setStatus] = useState(null);
|
||||
const [deviceConfig, setDeviceConfig] = useState(null);
|
||||
const [error, setError] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const getDevice = () => {
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${currentToken}`,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.get(`${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}`, options)
|
||||
.then((response) => {
|
||||
setDeviceConfig(response.data);
|
||||
})
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('device.error_fetching_device', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getData = () => {
|
||||
setLoading(true);
|
||||
const options = {
|
||||
@@ -45,9 +70,17 @@ const DeviceStatusCard = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const refresh = () => {
|
||||
getData();
|
||||
getDevice();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setError(false);
|
||||
if (deviceSerialNumber) getData();
|
||||
if (deviceSerialNumber) {
|
||||
getDevice();
|
||||
getData();
|
||||
}
|
||||
}, [deviceSerialNumber]);
|
||||
|
||||
return (
|
||||
@@ -56,7 +89,8 @@ const DeviceStatusCard = () => {
|
||||
loading={loading}
|
||||
error={error}
|
||||
deviceSerialNumber={deviceSerialNumber}
|
||||
getData={getData}
|
||||
getData={refresh}
|
||||
deviceConfig={deviceConfig}
|
||||
status={status}
|
||||
lastStats={lastStats}
|
||||
/>
|
||||
|
||||
@@ -145,10 +145,10 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
|
||||
getUsers();
|
||||
toggle();
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('user.update_failure_title'),
|
||||
body: t('user.update_failure'),
|
||||
body: t('user.update_failure', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
|
||||
@@ -32,10 +32,10 @@ const EventQueueModal = ({ show, toggle }) => {
|
||||
.then((response) => {
|
||||
setResult(response.data);
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('commands.unable_queue'),
|
||||
body: t('commands.unable_queue', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
|
||||
@@ -10,7 +10,10 @@ import {
|
||||
CForm,
|
||||
CSwitch,
|
||||
CAlert,
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -84,8 +87,15 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
||||
|
||||
return (
|
||||
<CModal show={show} onClose={toggleModal}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>{t('factory_reset.title')}</CModalTitle>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">{t('factory_reset.title')}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
{hadSuccess ? (
|
||||
<SuccessfulActionModalBody toggleModal={toggleModal} />
|
||||
|
||||
@@ -46,7 +46,7 @@ const FirmwareHistoryModal = ({ serialNumber, show, toggle }) => {
|
||||
return (
|
||||
<CModal size="xl" show={show} onClose={toggle} scrollable>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>
|
||||
<CModalTitle className="pl-1 pt-1">
|
||||
#{serialNumber} {t('firmware.history_title')}
|
||||
</CModalTitle>
|
||||
</CModalHeader>
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
import { CButton, CSpinner, CModalFooter } from '@coreui/react';
|
||||
|
||||
const UpgradeFooter = ({
|
||||
isNow,
|
||||
isShown,
|
||||
isLoading,
|
||||
action,
|
||||
color,
|
||||
variant,
|
||||
block,
|
||||
toggleParent,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [askingIfSure, setAskingIfSure] = useState(false);
|
||||
|
||||
const confirmingIfSure = () => {
|
||||
setAskingIfSure(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setAskingIfSure(false);
|
||||
}, [isShown]);
|
||||
|
||||
return (
|
||||
<CModalFooter>
|
||||
<div hidden={!askingIfSure}>{t('common.are_you_sure')}</div>
|
||||
<CButton
|
||||
disabled={isLoading}
|
||||
hidden={askingIfSure}
|
||||
color={color}
|
||||
variant={variant}
|
||||
onClick={() => confirmingIfSure()}
|
||||
block={block}
|
||||
>
|
||||
{isNow ? t('upgrade.upgrade') : t('common.schedule')}
|
||||
</CButton>
|
||||
<CButton
|
||||
disabled={isLoading}
|
||||
hidden={!askingIfSure}
|
||||
color={color}
|
||||
onClick={() => action()}
|
||||
block={block}
|
||||
>
|
||||
{isLoading ? t('common.loading_ellipsis') : t('common.yes')}
|
||||
<CSpinner color="light" hidden={!isLoading} component="span" size="sm" />
|
||||
</CButton>
|
||||
<CButton color="secondary" onClick={toggleParent}>
|
||||
{t('common.cancel')}
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
);
|
||||
};
|
||||
|
||||
UpgradeFooter.propTypes = {
|
||||
isNow: PropTypes.bool.isRequired,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
block: PropTypes.bool,
|
||||
action: PropTypes.func.isRequired,
|
||||
color: PropTypes.string,
|
||||
variant: PropTypes.string,
|
||||
toggleParent: PropTypes.func.isRequired,
|
||||
isShown: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
UpgradeFooter.defaultProps = {
|
||||
color: 'primary',
|
||||
variant: '',
|
||||
block: false,
|
||||
};
|
||||
|
||||
export default UpgradeFooter;
|
||||
@@ -1,99 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
import { CModalBody } from '@coreui/react';
|
||||
import { v4 as createUuid } from 'uuid';
|
||||
import { useAuth } from 'ucentral-libs';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
|
||||
const UpgradeWaitingBody = ({ serialNumber }) => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const [currentStep, setCurrentStep] = useState(0);
|
||||
const [secondsElapsed, setSecondsElapsed] = useState(0);
|
||||
const [labelsToShow, setLabelsToShow] = useState(['upgrade.command_submitted']);
|
||||
|
||||
const getDeviceConnection = () => {
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${currentToken}`,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.get(`${endpoints.owgw}/api/v1/device/${encodeURIComponent(serialNumber)}/status`, options)
|
||||
.then((response) => response.data.connected)
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const getFirmwareVersion = () => {
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${currentToken}`,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.get(`${endpoints.owgw}/api/v1/device/${encodeURIComponent(serialNumber)}`, options)
|
||||
.then((response) => response.data.firmware)
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const refreshStep = () => {
|
||||
if (currentStep === 0 && !getDeviceConnection) {
|
||||
const labelsToAdd = [
|
||||
t('upgrade.device_disconnected'),
|
||||
t('upgrade.device_upgrading_firmware'),
|
||||
t('upgrade.waiting_for_device'),
|
||||
];
|
||||
setLabelsToShow([...labelsToShow, ...labelsToAdd]);
|
||||
setCurrentStep(1);
|
||||
} else if (currentStep === 1 && getDeviceConnection()) {
|
||||
const newFirmware = `: ${getFirmwareVersion()}`;
|
||||
const labelsToAdd = [
|
||||
t('upgrade.device_reconnected'),
|
||||
`${t('upgrade.new_version')}: ${newFirmware}`,
|
||||
];
|
||||
setLabelsToShow([...labelsToShow, ...labelsToAdd]);
|
||||
setCurrentStep(2);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const refreshIntervalId = setInterval(() => {
|
||||
refreshStep();
|
||||
}, 5000);
|
||||
|
||||
const timerIntervalId = setInterval(() => {
|
||||
setSecondsElapsed(secondsElapsed + 1);
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
clearInterval(refreshIntervalId);
|
||||
clearInterval(timerIntervalId);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CModalBody>
|
||||
<div className="consoleBox">
|
||||
{labelsToShow.map((label) => (
|
||||
<p key={createUuid()}>
|
||||
{new Date().toString()}:{label}
|
||||
</p>
|
||||
))}
|
||||
<p>
|
||||
{t('common.seconds_elapsed')}:{secondsElapsed}
|
||||
</p>
|
||||
</div>
|
||||
</CModalBody>
|
||||
);
|
||||
};
|
||||
|
||||
UpgradeWaitingBody.propTypes = {
|
||||
serialNumber: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default UpgradeWaitingBody;
|
||||
@@ -1,241 +0,0 @@
|
||||
import {
|
||||
CButton,
|
||||
CModal,
|
||||
CModalHeader,
|
||||
CModalTitle,
|
||||
CModalBody,
|
||||
CSwitch,
|
||||
CCol,
|
||||
CRow,
|
||||
CInput,
|
||||
CInvalidFeedback,
|
||||
CModalFooter,
|
||||
} from '@coreui/react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
import PropTypes from 'prop-types';
|
||||
import { dateToUnix } from 'utils/helper';
|
||||
import 'react-widgets/styles.css';
|
||||
import { useDevice, useAuth } from 'ucentral-libs';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import eventBus from 'utils/eventBus';
|
||||
import ButtonFooter from './UpgradeFooter';
|
||||
import UpgradeWaitingBody from './UpgradeWaitingBody';
|
||||
|
||||
const FirmwareUpgradeModal = ({ show, toggleModal }) => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const { deviceSerialNumber, getDeviceConnection } = useDevice();
|
||||
const [isNow, setIsNow] = useState(true);
|
||||
const [waitForUpgrade, setWaitForUpgrade] = useState(false);
|
||||
const [date, setDate] = useState(new Date().toString());
|
||||
const [firmware, setFirmware] = useState('');
|
||||
const [validFirmware, setValidFirmware] = useState(true);
|
||||
const [validDate, setValidDate] = useState(true);
|
||||
const [blockFields, setBlockFields] = useState(false);
|
||||
const [disabledWaiting, setDisableWaiting] = useState(false);
|
||||
const [waitingForUpgrade, setWaitingForUpgrade] = useState(false);
|
||||
const [showWaitingConsole, setShowWaitingConsole] = useState(false);
|
||||
const [deviceConnected, setDeviceConnected] = useState(true);
|
||||
|
||||
const toggleNow = () => {
|
||||
if (isNow) {
|
||||
setWaitForUpgrade(false);
|
||||
setDisableWaiting(true);
|
||||
} else {
|
||||
setDisableWaiting(false);
|
||||
}
|
||||
|
||||
setIsNow(!isNow);
|
||||
};
|
||||
|
||||
const toggleWaitForUpgrade = () => {
|
||||
setWaitForUpgrade(waitForUpgrade);
|
||||
};
|
||||
|
||||
const formValidation = () => {
|
||||
let valid = true;
|
||||
if (firmware.trim() === '') {
|
||||
setValidFirmware(false);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (!isNow && date.trim() === '') {
|
||||
setValidDate(false);
|
||||
valid = false;
|
||||
}
|
||||
return valid;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setBlockFields(false);
|
||||
setShowWaitingConsole(false);
|
||||
}, [show]);
|
||||
|
||||
useEffect(() => {
|
||||
setValidFirmware(true);
|
||||
setValidDate(true);
|
||||
}, [firmware, date]);
|
||||
|
||||
useEffect(() => {
|
||||
if (deviceSerialNumber !== null && show) {
|
||||
const asyncGet = async () => {
|
||||
const isConnected = await getDeviceConnection(
|
||||
deviceSerialNumber,
|
||||
currentToken,
|
||||
endpoints.owgw,
|
||||
);
|
||||
setDisableWaiting(!isConnected);
|
||||
setDeviceConnected(isConnected);
|
||||
};
|
||||
asyncGet();
|
||||
}
|
||||
}, [show]);
|
||||
|
||||
const postUpgrade = () => {
|
||||
if (formValidation()) {
|
||||
setWaitingForUpgrade(true);
|
||||
setBlockFields(true);
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${currentToken}`,
|
||||
serialNumber: deviceSerialNumber,
|
||||
};
|
||||
|
||||
const parameters = {
|
||||
serialNumber: deviceSerialNumber,
|
||||
when: isNow ? 0 : dateToUnix(date),
|
||||
uri: firmware,
|
||||
};
|
||||
axiosInstance
|
||||
.post(
|
||||
`${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/upgrade`,
|
||||
parameters,
|
||||
{ headers },
|
||||
)
|
||||
.then(() => {
|
||||
if (waitForUpgrade) {
|
||||
setShowWaitingConsole(true);
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
setBlockFields(false);
|
||||
setWaitingForUpgrade(false);
|
||||
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (showWaitingConsole) {
|
||||
return (
|
||||
<CModal show={show} onClose={toggleModal}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>{t('upgrade.title')}</CModalTitle>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<UpgradeWaitingBody serialNumber={deviceSerialNumber} />
|
||||
</CModalBody>
|
||||
<CModalFooter>
|
||||
<CButton color="secondary" onClick={toggleModal}>
|
||||
{t('common.close')}
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
</CModal>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CModal show={show} onClose={toggleModal}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>{t('upgrade.title')}</CModalTitle>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<h6>{t('upgrade.directions')}</h6>
|
||||
<CRow className="mt-3">
|
||||
<CCol md="4" className="mt-2">
|
||||
<p>{t('upgrade.firmware_uri')}</p>
|
||||
</CCol>
|
||||
<CCol md="8">
|
||||
<CInput
|
||||
disabled={blockFields}
|
||||
className={`form-control ${!validFirmware ? 'is-invalid' : ''}`}
|
||||
type="text"
|
||||
id="uri"
|
||||
name="uri-input"
|
||||
autoComplete="firmware-uri"
|
||||
onChange={(event) => setFirmware(event.target.value)}
|
||||
value={firmware}
|
||||
/>
|
||||
<CInvalidFeedback>{t('upgrade.need_uri')}</CInvalidFeedback>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-3">
|
||||
<CCol md="8">
|
||||
<p>{t('common.execute_now')}</p>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CSwitch
|
||||
disabled={blockFields}
|
||||
color="primary"
|
||||
defaultChecked={isNow}
|
||||
onClick={toggleNow}
|
||||
labelOn={t('common.yes')}
|
||||
labelOff={t('common.no')}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-3" hidden={isNow}>
|
||||
<CCol md="4" className="mt-2">
|
||||
<p>{t('upgrade.time')}</p>
|
||||
</CCol>
|
||||
<CCol xs="12" md="8">
|
||||
<DatePicker
|
||||
selected={new Date(date)}
|
||||
value={new Date(date)}
|
||||
className={`form-control ${!validDate ? 'is-invalid' : ''}`}
|
||||
includeTime
|
||||
disabled={blockFields}
|
||||
onChange={(newDate) => setDate(newDate.toString())}
|
||||
/>
|
||||
<CInvalidFeedback>{t('common.need_date')}</CInvalidFeedback>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-3" hidden={true || !isNow || disabledWaiting || !deviceConnected}>
|
||||
<CCol md="8">
|
||||
<p>
|
||||
{t('upgrade.wait_for_upgrade')}
|
||||
<b hidden={!disabledWaiting}> {t('upgrade.offline_device')}</b>
|
||||
</p>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CSwitch
|
||||
disabled={blockFields || disabledWaiting}
|
||||
color="primary"
|
||||
defaultChecked={waitForUpgrade}
|
||||
onClick={toggleWaitForUpgrade}
|
||||
labelOn={t('common.yes')}
|
||||
labelOff={t('common.no')}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CModalBody>
|
||||
<ButtonFooter
|
||||
isNow={isNow}
|
||||
isShown={show}
|
||||
isLoading={waitingForUpgrade}
|
||||
action={postUpgrade}
|
||||
color="primary"
|
||||
toggleParent={toggleModal}
|
||||
/>
|
||||
</CModal>
|
||||
);
|
||||
};
|
||||
|
||||
FirmwareUpgradeModal.propTypes = {
|
||||
show: PropTypes.bool.isRequired,
|
||||
toggleModal: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default FirmwareUpgradeModal;
|
||||
@@ -1,12 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
CButton,
|
||||
CModal,
|
||||
CModalHeader,
|
||||
CModalBody,
|
||||
CModalTitle,
|
||||
CModalFooter,
|
||||
} from '@coreui/react';
|
||||
import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
@@ -45,17 +40,19 @@ const LatestStatisticsModal = ({ show, toggle }) => {
|
||||
|
||||
return (
|
||||
<CModal size="lg" show={show} onClose={toggle}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="text-dark">{t('statistics.latest_statistics')}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<pre className="ignore">{JSON.stringify(latestStats, null, 4)}</pre>
|
||||
</CModalBody>
|
||||
<CModalFooter>
|
||||
<CButton color="secondary" onClick={toggle}>
|
||||
{t('common.close')}
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
</CModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { CCard, CCardHeader, CCardBody, CRow, CCol, CPopover, CButton } from '@coreui/react';
|
||||
import { CCard, CCardHeader, CCardBody, CPopover, CButton } from '@coreui/react';
|
||||
import { cilSync } from '@coreui/icons';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import eventBus from 'utils/eventBus';
|
||||
@@ -36,38 +36,31 @@ const DeviceStatisticsCard = () => {
|
||||
<div>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<CRow>
|
||||
<CCol>
|
||||
<div className="text-value-xxl pt-2">{t('statistics.title')}</div>
|
||||
</CCol>
|
||||
<CCol sm="6" xxl="6">
|
||||
<CRow>
|
||||
<CCol sm="1" xxl="5" />
|
||||
<CCol sm="4" xxl="2" className="text-right">
|
||||
<CButton color="secondary" onClick={goToAnalysis}>
|
||||
{t('wifi_analysis.title')}
|
||||
</CButton>
|
||||
</CCol>
|
||||
<CCol sm="3" xxl="2" className="text-right">
|
||||
<CButton color="secondary" onClick={toggleLatestModal}>
|
||||
{t('statistics.show_latest')}
|
||||
</CButton>
|
||||
</CCol>
|
||||
<CCol sm="3" xxl="2" className="text-right">
|
||||
<CButton color="secondary" onClick={toggleLifetimeModal}>
|
||||
Lifetime Statistics
|
||||
</CButton>
|
||||
</CCol>
|
||||
<CCol sm="1" xxl="1" className="text-center">
|
||||
<CPopover content={t('common.refresh')}>
|
||||
<CButton color="secondary" onClick={refresh} size="sm">
|
||||
<CIcon content={cilSync} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<div className="d-flex flex-row-reverse align-items-center">
|
||||
<div className="pl-2">
|
||||
<CPopover content={t('common.refresh')}>
|
||||
<CButton color="primary" variant="outline" onClick={refresh}>
|
||||
<CIcon content={cilSync} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
<div className="pl-2">
|
||||
<CButton color="primary" variant="outline" onClick={toggleLifetimeModal}>
|
||||
Lifetime Statistics
|
||||
</CButton>
|
||||
</div>
|
||||
<div className="pl-2">
|
||||
<CButton color="primary" variant="outline" onClick={toggleLatestModal}>
|
||||
{t('statistics.show_latest')}
|
||||
</CButton>
|
||||
</div>
|
||||
<div>
|
||||
<CButton color="primary" variant="outline" onClick={goToAnalysis}>
|
||||
{t('wifi_analysis.title')}
|
||||
</CButton>
|
||||
</div>
|
||||
<div className="text-value-lg mr-auto">{t('statistics.title')}</div>
|
||||
</div>
|
||||
</CCardHeader>
|
||||
<CCardBody className="p-5">
|
||||
<StatisticsChartList />
|
||||
|
||||
@@ -8,7 +8,10 @@ import {
|
||||
CSwitch,
|
||||
CCol,
|
||||
CRow,
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
@@ -82,8 +85,15 @@ const ActionModal = ({ show, toggleModal }) => {
|
||||
|
||||
return (
|
||||
<CModal show={show} onClose={toggleModal}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>{t('reboot.title')}</CModalTitle>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">{t('reboot.title')}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
{result === 'success' ? (
|
||||
<SuccessfulActionModalBody toggleModal={toggleModal} />
|
||||
|
||||
@@ -13,7 +13,10 @@ import {
|
||||
CInputRadio,
|
||||
CFormGroup,
|
||||
CLabel,
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -256,8 +259,15 @@ const TraceModal = ({ show, toggleModal }) => {
|
||||
|
||||
return (
|
||||
<CModal show={show} onClose={toggleModal}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>{t('trace.title')}</CModalTitle>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">{t('trace.title')}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
{getBody()}
|
||||
</CModal>
|
||||
|
||||
@@ -10,7 +10,10 @@ import {
|
||||
CSwitch,
|
||||
CCol,
|
||||
CSpinner,
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -124,8 +127,15 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
||||
|
||||
return (
|
||||
<CModal size="lg" show={show} onClose={toggleModal}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>{t('actions.wifi_scan')}</CModalTitle>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">{t('actions.wifi_scan')}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<div hidden={hideOptions || waiting}>
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
/* eslint-disable-rule prefer-destructuring */
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
CButton,
|
||||
CModal,
|
||||
CModalHeader,
|
||||
CModalBody,
|
||||
CModalTitle,
|
||||
CModalFooter,
|
||||
} from '@coreui/react';
|
||||
import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
import PropTypes from 'prop-types';
|
||||
import { prettyDate } from 'utils/helper';
|
||||
import WifiChannelTable from './WifiChannelTable';
|
||||
@@ -48,21 +43,23 @@ const WifiScanResultModal = ({ show, toggle, scanResults, date }) => {
|
||||
};
|
||||
return (
|
||||
<CModal size="lg" show={show} onClose={toggle}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalHeader>
|
||||
<CModalTitle className="text-dark">
|
||||
{date !== '' ? prettyDate(date) : ''} {t('scan.results')}
|
||||
</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
{scanResults === null ? null : (
|
||||
<WifiChannelTable channels={parseThroughList(scanResults)} />
|
||||
)}
|
||||
</CModalBody>
|
||||
<CModalFooter>
|
||||
<CButton color="secondary" onClick={toggle}>
|
||||
{t('common.close')}
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
</CModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -89,7 +89,7 @@ const TheLayout = () => {
|
||||
<PageContainer t={t} routes={routes} redirectTo="/devices" />
|
||||
</ToastProvider>
|
||||
</div>
|
||||
<Footer t={t} version="2.1.10" />
|
||||
<Footer t={t} version={process.env.VERSION} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { CRow, CCol } from '@coreui/react';
|
||||
import DeviceHealth from 'components/DeviceHealth';
|
||||
import DeviceConfiguration from 'components/DeviceConfiguration';
|
||||
import DeviceStatusCard from 'components/DeviceStatusCard';
|
||||
import CommandHistory from 'components/CommandHistory';
|
||||
import DeviceLogs from 'components/DeviceLogs';
|
||||
@@ -18,20 +17,25 @@ const DevicePage = () => {
|
||||
<div className="App">
|
||||
<DeviceProvider axiosInstance={axiosInstance} serialNumber={deviceId}>
|
||||
<CRow>
|
||||
<CCol xs="12" lg="6">
|
||||
<CCol>
|
||||
<DeviceStatusCard />
|
||||
<DeviceConfiguration />
|
||||
</CCol>
|
||||
<CCol xs="12" lg="6">
|
||||
<DeviceLogs />
|
||||
<DeviceHealth />
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol>
|
||||
<CommandHistory />
|
||||
</CCol>
|
||||
<CCol>
|
||||
<DeviceActionCard />
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol>
|
||||
<DeviceStatisticsCard />
|
||||
<CommandHistory />
|
||||
</CCol>
|
||||
<CCol>
|
||||
<DeviceHealth />
|
||||
<DeviceLogs />
|
||||
</CCol>
|
||||
</CRow>
|
||||
</DeviceProvider>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CCard, CCardBody } from '@coreui/react';
|
||||
import { CCard, CCardBody, CCardHeader, CButton, CPopover } from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilSave } from '@coreui/icons';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import { testRegex } from 'utils/helper';
|
||||
import { useUser, EditMyProfile, useAuth, useToast } from 'ucentral-libs';
|
||||
@@ -122,10 +124,10 @@ const ProfilePage = () => {
|
||||
setNewAvatarFile(null);
|
||||
setFileInputKey(fileInputKey + 1);
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('user.update_failure_title'),
|
||||
body: t('user.update_failure'),
|
||||
body: t('user.update_failure', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
@@ -180,10 +182,10 @@ const ProfilePage = () => {
|
||||
autohide: true,
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('user.update_failure_title'),
|
||||
body: t('user.update_failure'),
|
||||
body: t('user.update_failure', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
@@ -260,12 +262,20 @@ const ProfilePage = () => {
|
||||
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.save')}>
|
||||
<CButton onClick={updateUser} color="primary" variant="outline" disabled={loading}>
|
||||
<CIcon content={cilSave} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<EditMyProfile
|
||||
t={t}
|
||||
user={userForm}
|
||||
updateUserWithId={updateWithId}
|
||||
saveUser={updateUser}
|
||||
loading={loading}
|
||||
policies={policies}
|
||||
addNote={addNote}
|
||||
|
||||
@@ -45,7 +45,13 @@ const UserListPage = () => {
|
||||
.then((response) => {
|
||||
setUsers(response.data.users);
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('user.error_fetching_users', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
@@ -113,7 +119,13 @@ const UserListPage = () => {
|
||||
setUsersToDisplay(newUsers);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('user.error_fetching_users', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
@@ -139,10 +151,10 @@ const UserListPage = () => {
|
||||
});
|
||||
getUsers();
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((e) => {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('user.delete_failure'),
|
||||
body: t('user.delete_failure', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
@@ -186,6 +198,7 @@ const UserListPage = () => {
|
||||
usersPerPage={usersPerPage}
|
||||
setUsersPerPage={updateUsersPerPage}
|
||||
pageCount={pageCount}
|
||||
currentPage={page.selected}
|
||||
setPage={setPage}
|
||||
deleteUser={deleteUser}
|
||||
deleteLoading={deleteLoading}
|
||||
|
||||
@@ -14,8 +14,12 @@ import {
|
||||
CModal,
|
||||
CModalHeader,
|
||||
CModalBody,
|
||||
CModalTitle,
|
||||
CRow,
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
|
||||
const WifiAnalysisPage = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -198,7 +202,7 @@ const WifiAnalysisPage = () => {
|
||||
<h5 className="mb-0">{t('common.device', { serialNumber: deviceId })}</h5>
|
||||
</CCol>
|
||||
<CCol className="text-right">
|
||||
<CButton color="secondary" onClick={toggleModal}>
|
||||
<CButton color="primary" variant="outline" onClick={toggleModal}>
|
||||
{t('wifi_analysis.network_diagram')}
|
||||
</CButton>
|
||||
</CCol>
|
||||
@@ -235,7 +239,16 @@ const WifiAnalysisPage = () => {
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
<CModal size="xl" show={showModal} onClose={toggleModal}>
|
||||
<CModalHeader closeButton>{t('wifi_analysis.network_diagram')}</CModalHeader>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">{t('wifi_analysis.network_diagram')}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
{showModal ? (
|
||||
<NetworkDiagram
|
||||
|
||||
Reference in New Issue
Block a user