mirror of
				https://github.com/optim-enterprises-bv/OptimCloud-gw-ui.git
				synced 2025-10-30 01:42:19 +00:00 
			
		
		
		
	[WIFI-13170] Advanced system page
Signed-off-by: Charles <charles.bourque96@gmail.com>
This commit is contained in:
		
							
								
								
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,12 +1,12 @@ | ||||
| { | ||||
|   "name": "ucentral-client", | ||||
|   "version": "2.11.0(16)", | ||||
|   "version": "3.0.0(1)", | ||||
|   "lockfileVersion": 3, | ||||
|   "requires": true, | ||||
|   "packages": { | ||||
|     "": { | ||||
|       "name": "ucentral-client", | ||||
|       "version": "2.11.0(16)", | ||||
|       "version": "3.0.0(1)", | ||||
|       "license": "ISC", | ||||
|       "dependencies": { | ||||
|         "@chakra-ui/anatomy": "^2.1.1", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "ucentral-client", | ||||
|   "version": "2.11.0(16)", | ||||
|   "version": "3.0.0(1)", | ||||
|   "description": "", | ||||
|   "private": true, | ||||
|   "main": "index.tsx", | ||||
|   | ||||
| @@ -269,6 +269,7 @@ | ||||
| 		"map": "Karte", | ||||
| 		"max": "Max", | ||||
| 		"min": "MINDEST", | ||||
| 		"miscellaneous": "Verschiedenes", | ||||
| 		"mode": "Modus", | ||||
| 		"model": "Modell", | ||||
| 		"modified": "Geändert", | ||||
| @@ -737,6 +738,7 @@ | ||||
| 	"form": { | ||||
| 		"captive_web_root_explanation": "Bitte verwenden Sie nur .tar-Dateien (keine komprimierten Dateien wie z. B. .targz)", | ||||
| 		"certificate_file_explanation": "Bitte verwenden Sie eine .pem-Datei, die mit „-----BEGIN CERTIFICATE-----“ beginnt und mit „-----END CERTIFICATE-----“ endet.", | ||||
| 		"invalid_alphanumeric_with_dash": "Akzeptierte Zeichen. sind nur alphanumerisch (Buchstaben & Zahlen)", | ||||
| 		"invalid_cidr": "Ungültige CIDR-IPv4-Adresse. Beispiel: 192.168.0.1/12", | ||||
| 		"invalid_email": "Ungültige E-Mail", | ||||
| 		"invalid_file_content": "Ungültiger Dateiinhalt, bitte bestätigen Sie, dass es sich um ein gültiges Format handelt", | ||||
| @@ -763,7 +765,11 @@ | ||||
| 		"invalid_static_ipv4_e": "Ungültige Adresse, dieser Bereich ist für Experimente reserviert (Klasse E). Das erste Oktett sollte 223 oder niedriger sein", | ||||
| 		"invalid_third_party": "Ungültige Drittanbieter-JSON-Zeichenfolge. Bitte bestätigen Sie, dass Ihr Wert ein gültiges JSON ist", | ||||
| 		"key_file_explanation": "Bitte verwenden Sie eine .pem-Datei, die mit „-----BEGIN PRIVATE KEY-----“ beginnt und mit „-----END PRIVATE KEY-----“ endet.", | ||||
| 		"max_length": "Maximale Länge von {{max}} Zeichen.", | ||||
| 		"max_value": "Maximalwert von {{max}}", | ||||
| 		"min_length": "Mindestlänge von {{min}} Zeichen.", | ||||
| 		"min_max_string": "Der Wert muss eine Länge zwischen {{min}} (einschließlich) und {{max}} (einschließlich) haben.", | ||||
| 		"min_value": "Mindestwert von {{min}}", | ||||
| 		"missing_interface_upstream": "Sie müssen mindestens eine Upstream-Schnittstelle haben. Im Moment sind alle Ihre Schnittstellen nachgelagert", | ||||
| 		"new_email_to_notify": "Neue E-Mail zur Benachrichtigung", | ||||
| 		"new_phone_to_notify": "Neues Telefon zu benachrichtigen", | ||||
| @@ -905,6 +911,11 @@ | ||||
| 		"one": "Benachrichtigung", | ||||
| 		"other": "Benachrichtigungen" | ||||
| 	}, | ||||
| 	"openroaming": { | ||||
| 		"pool_strategy": "Pool-Strategie", | ||||
| 		"radius_endpoint_one": "Radiusendpunkt", | ||||
| 		"radius_endpoint_other": "Radiusendpunkte" | ||||
| 	}, | ||||
| 	"operator": { | ||||
| 		"delete_explanation": "Möchten Sie diesen Operator wirklich löschen? Dieser Vorgang ist nicht umkehrbar", | ||||
| 		"delete_operator": "Betreiber löschen", | ||||
| @@ -970,6 +981,27 @@ | ||||
| 		"title": "Beschränkungen", | ||||
| 		"tty": "TTY-Zugriff" | ||||
| 	}, | ||||
| 	"roaming": { | ||||
| 		"account_created": "Neues Konto erstellt!", | ||||
| 		"account_deleted": "Konto gelöscht!", | ||||
| 		"account_one": "Konto", | ||||
| 		"account_other": "Konten", | ||||
| 		"certificate_deleted": "Zertifikat gelöscht!", | ||||
| 		"certificate_one": "Zertifikat", | ||||
| 		"certificate_other": "Zertifikate", | ||||
| 		"city": "Stadt", | ||||
| 		"common_name": "Gemeinsamen Namen", | ||||
| 		"country": "Land", | ||||
| 		"global_reach": "Globale Reichweite", | ||||
| 		"global_reach_account_id": "Konto-ID", | ||||
| 		"invalid_certificate": "Ungültiges Zertifikat", | ||||
| 		"invalid_key": "Ungültiger privater Schlüssel", | ||||
| 		"location_details_title": "Ort", | ||||
| 		"organization": "Organisation", | ||||
| 		"private_key": "Privat Schlüssel", | ||||
| 		"province": "Provinz", | ||||
| 		"state": "Zustand" | ||||
| 	}, | ||||
| 	"rrm": { | ||||
| 		"algorithm": "Algorithmus", | ||||
| 		"algorithm_other": "Algorithmen", | ||||
| @@ -1091,6 +1123,7 @@ | ||||
| 		"title": "Abonnenten" | ||||
| 	}, | ||||
| 	"system": { | ||||
| 		"advanced": "Erweitert", | ||||
| 		"backend_logs": "Back-End-Protokolle", | ||||
| 		"configuration": "Aufbau", | ||||
| 		"could_not_retrieve": "Fehler: {{name}} Systeminformationen konnten nicht abgerufen werden", | ||||
|   | ||||
| @@ -269,6 +269,7 @@ | ||||
| 		"map": "Map", | ||||
| 		"max": "Max", | ||||
| 		"min": "Min", | ||||
| 		"miscellaneous": "Miscellaneous", | ||||
| 		"mode": "Mode", | ||||
| 		"model": "Model", | ||||
| 		"modified": "Modified", | ||||
| @@ -737,6 +738,7 @@ | ||||
| 	"form": { | ||||
| 		"captive_web_root_explanation": "Please use .tar files only (no compressed files like .targz, for example)", | ||||
| 		"certificate_file_explanation": "Please use a .pem file that starts with \"-----BEGIN CERTIFICATE-----\" and ends with \"-----END CERTIFICATE-----\"", | ||||
| 		"invalid_alphanumeric_with_dash": "Accepted chars. are only alphanumeric (letters & numbers)", | ||||
| 		"invalid_cidr": "Invalid CIDR IPv4 address. Example: 192.168.0.1/12", | ||||
| 		"invalid_email": "Invalid Email", | ||||
| 		"invalid_file_content": "Invalid file content, please confirm that it is of the valid format", | ||||
| @@ -763,7 +765,11 @@ | ||||
| 		"invalid_static_ipv4_e": "Invalid address, this range reserved for experiments (class E). The first octet should be 223 or lower", | ||||
| 		"invalid_third_party": "Invalid Third Party JSON string. Please confirm that your value is a valid JSON", | ||||
| 		"key_file_explanation": "Please use a .pem file that starts with \"-----BEGIN PRIVATE KEY-----\" and ends with \"-----END PRIVATE KEY-----\"", | ||||
| 		"max_length": "Maximum length of {{max}} chars.", | ||||
| 		"max_value": "Maximum value of {{max}}", | ||||
| 		"min_length": "Minimum length of {{min}} chars.", | ||||
| 		"min_max_string": "Value needs to be of a length between {{min}} (inclusive) and {{max}} (inclusive)", | ||||
| 		"min_value": "Minimum value of {{min}}", | ||||
| 		"missing_interface_upstream": "You need to have at least one upstream interface. At the moment, all your interfaces are downstream", | ||||
| 		"new_email_to_notify": "New email to notify", | ||||
| 		"new_phone_to_notify": "New phone to notify", | ||||
| @@ -905,6 +911,11 @@ | ||||
| 		"one": "Notification", | ||||
| 		"other": "Notifications" | ||||
| 	}, | ||||
| 	"openroaming": { | ||||
| 		"pool_strategy": "Pool Strategy", | ||||
| 		"radius_endpoint_one": "Radius Endpoint", | ||||
| 		"radius_endpoint_other": "Radius Endpoints" | ||||
| 	}, | ||||
| 	"operator": { | ||||
| 		"delete_explanation": "Are you sure you want to delete this operator? This operation is not reversible", | ||||
| 		"delete_operator": "Delete Operator", | ||||
| @@ -970,6 +981,27 @@ | ||||
| 		"title": "Restrictions", | ||||
| 		"tty": "TTY Access" | ||||
| 	}, | ||||
| 	"roaming": { | ||||
| 		"account_created": "New account created!", | ||||
| 		"account_deleted": "Deleted account!", | ||||
| 		"account_one": "Account", | ||||
| 		"account_other": "Accounts", | ||||
| 		"certificate_deleted": "Deleted certificate!", | ||||
| 		"certificate_one": "Certificate", | ||||
| 		"certificate_other": "Certificates", | ||||
| 		"city": "City", | ||||
| 		"common_name": "Common Name", | ||||
| 		"country": "Country", | ||||
| 		"global_reach": "GlobalReach", | ||||
| 		"global_reach_account_id": " Account ID", | ||||
| 		"invalid_certificate": "Invalid certificate", | ||||
| 		"invalid_key": "Invalid private key", | ||||
| 		"location_details_title": "Location", | ||||
| 		"organization": "Organization", | ||||
| 		"private_key": "Private Key", | ||||
| 		"province": "Province", | ||||
| 		"state": "State" | ||||
| 	}, | ||||
| 	"rrm": { | ||||
| 		"algorithm": "Algorithm", | ||||
| 		"algorithm_other": "Algorithms", | ||||
| @@ -1091,6 +1123,7 @@ | ||||
| 		"title": "Subscribers" | ||||
| 	}, | ||||
| 	"system": { | ||||
| 		"advanced": "Advanced", | ||||
| 		"backend_logs": "Back-End Logs", | ||||
| 		"configuration": "Configuration", | ||||
| 		"could_not_retrieve": "Error: could not retrieve {{name}} system information", | ||||
|   | ||||
| @@ -269,6 +269,7 @@ | ||||
| 		"map": "Mapa", | ||||
| 		"max": "Max", | ||||
| 		"min": "Min", | ||||
| 		"miscellaneous": "Diverso", | ||||
| 		"mode": "Modo", | ||||
| 		"model": "Modelo", | ||||
| 		"modified": "Modificado", | ||||
| @@ -737,6 +738,7 @@ | ||||
| 	"form": { | ||||
| 		"captive_web_root_explanation": "Utilice únicamente archivos .tar (no archivos comprimidos como .targz, por ejemplo)", | ||||
| 		"certificate_file_explanation": "Utilice un archivo .pem que comience con \"-----BEGIN CERTIFICATE-----\" y termine con \"-----END CERTIFICATE-----\"", | ||||
| 		"invalid_alphanumeric_with_dash": "Caracteres aceptados. son solo alfanuméricos (letras y números)", | ||||
| 		"invalid_cidr": "Dirección IPv4 CIDR no válida. Ejemplo: 192.168.0.1/12", | ||||
| 		"invalid_email": "Email inválido", | ||||
| 		"invalid_file_content": "Contenido de archivo no válido, confirme que tiene un formato válido", | ||||
| @@ -763,7 +765,11 @@ | ||||
| 		"invalid_static_ipv4_e": "Dirección no válida, este rango reservado para experimentos (clase E). El primer octeto debe ser 223 o inferior", | ||||
| 		"invalid_third_party": "Cadena JSON de terceros no válida. Confirme que su valor es un JSON válido", | ||||
| 		"key_file_explanation": "Utilice un archivo .pem que comience con \"-----BEGIN PRIVATE KEY-----\" y termine con \"-----END PRIVATE KEY-----\"", | ||||
| 		"max_length": "Longitud máxima de {{max}} caracteres.", | ||||
| 		"max_value": "Valor máximo de {{max}}", | ||||
| 		"min_length": "Longitud mínima de {{min}} caracteres.", | ||||
| 		"min_max_string": "El valor debe tener una longitud entre {{min}} (inclusive) y {{max}} (inclusive)", | ||||
| 		"min_value": "Valor mínimo de {{min}}", | ||||
| 		"missing_interface_upstream": "Debe tener al menos una interfaz ascendente. Por el momento, todas sus interfaces están en sentido descendente", | ||||
| 		"new_email_to_notify": "Nuevo correo electrónico para notificar", | ||||
| 		"new_phone_to_notify": "Nuevo teléfono para avisar", | ||||
| @@ -905,6 +911,11 @@ | ||||
| 		"one": "Notificación", | ||||
| 		"other": "Notificaciones" | ||||
| 	}, | ||||
| 	"openroaming": { | ||||
| 		"pool_strategy": "Estrategia de piscina", | ||||
| 		"radius_endpoint_one": "Punto final del radio", | ||||
| 		"radius_endpoint_other": "Puntos finales de radio" | ||||
| 	}, | ||||
| 	"operator": { | ||||
| 		"delete_explanation": "¿Está seguro de que desea eliminar este operador? Esta operación no es reversible.", | ||||
| 		"delete_operator": "Eliminar operador", | ||||
| @@ -970,6 +981,27 @@ | ||||
| 		"title": "Las restricciones", | ||||
| 		"tty": "Acceso TTY" | ||||
| 	}, | ||||
| 	"roaming": { | ||||
| 		"account_created": "¡Nueva cuenta creada!", | ||||
| 		"account_deleted": "¡Cuenta eliminada!", | ||||
| 		"account_one": "Cuenta", | ||||
| 		"account_other": "Cuentas", | ||||
| 		"certificate_deleted": "Certificado eliminado!", | ||||
| 		"certificate_one": "Certificado", | ||||
| 		"certificate_other": "Certificados", | ||||
| 		"city": "ciudad", | ||||
| 		"common_name": "Nombre común", | ||||
| 		"country": "País", | ||||
| 		"global_reach": "Alcance global", | ||||
| 		"global_reach_account_id": "ID de cuenta ", | ||||
| 		"invalid_certificate": "Certificado inválido", | ||||
| 		"invalid_key": "Clave privada no válida", | ||||
| 		"location_details_title": "Ubicación", | ||||
| 		"organization": "Organización", | ||||
| 		"private_key": "Llave privada", | ||||
| 		"province": "Provincia", | ||||
| 		"state": "Estado" | ||||
| 	}, | ||||
| 	"rrm": { | ||||
| 		"algorithm": "Algoritmo", | ||||
| 		"algorithm_other": "Algoritmos", | ||||
| @@ -1091,6 +1123,7 @@ | ||||
| 		"title": "Suscriptores" | ||||
| 	}, | ||||
| 	"system": { | ||||
| 		"advanced": "Avanzado", | ||||
| 		"backend_logs": "Registros de back-end", | ||||
| 		"configuration": "Configuración", | ||||
| 		"could_not_retrieve": "Error: no se pudo recuperar la información del sistema {{name}} ", | ||||
|   | ||||
| @@ -269,6 +269,7 @@ | ||||
| 		"map": "Carte", | ||||
| 		"max": "Max", | ||||
| 		"min": "Min", | ||||
| 		"miscellaneous": "Divers", | ||||
| 		"mode": "Mode", | ||||
| 		"model": "Modèle", | ||||
| 		"modified": "Modifié", | ||||
| @@ -737,6 +738,7 @@ | ||||
| 	"form": { | ||||
| 		"captive_web_root_explanation": "Veuillez utiliser uniquement des fichiers .tar (pas de fichiers compressés comme .targz, par exemple)", | ||||
| 		"certificate_file_explanation": "Veuillez utiliser un fichier .pem qui commence par \"-----BEGIN CERTIFICATE-----\" et se termine par \"-----END CERTIFICATE-----\"", | ||||
| 		"invalid_alphanumeric_with_dash": "Caractères acceptés. sont uniquement alphanumériques (lettres et chiffres)", | ||||
| 		"invalid_cidr": "Adresse IPv4 CIDR non valide. Exemple : 192.168.0.1/12", | ||||
| 		"invalid_email": "Email Invalide", | ||||
| 		"invalid_file_content": "Contenu de fichier non valide, veuillez confirmer qu'il est au format valide", | ||||
| @@ -763,7 +765,11 @@ | ||||
| 		"invalid_static_ipv4_e": "Adresse invalide, cette plage est réservée aux expérimentations (classe E). Le premier octet doit être 223 ou moins", | ||||
| 		"invalid_third_party": "Chaîne JSON tierce non valide. Veuillez confirmer que votre valeur est un JSON valide", | ||||
| 		"key_file_explanation": "Veuillez utiliser un fichier .pem qui commence par \"-----BEGIN PRIVATE KEY-----\" et se termine par \"-----END PRIVATE KEY-----\"", | ||||
| 		"max_length": "Longueur maximale de {{max}}  caractères.", | ||||
| 		"max_value": "Valeur maximale de  {{max}}", | ||||
| 		"min_length": "Longueur minimale de {{min}}  caractères.", | ||||
| 		"min_max_string": "La valeur doit être d'une longueur comprise entre {{min}} (inclus) et {{max}} (inclus)", | ||||
| 		"min_value": "Valeur minimale de  {{min}}", | ||||
| 		"missing_interface_upstream": "Vous devez avoir au moins une interface en amont. Pour le moment, toutes vos interfaces sont en aval", | ||||
| 		"new_email_to_notify": "Nouvel e-mail à notifier", | ||||
| 		"new_phone_to_notify": "Nouveau téléphone à notifier", | ||||
| @@ -905,6 +911,11 @@ | ||||
| 		"one": "Notification", | ||||
| 		"other": "Les notifications" | ||||
| 	}, | ||||
| 	"openroaming": { | ||||
| 		"pool_strategy": "Stratégie de pool", | ||||
| 		"radius_endpoint_one": "Point final de rayon", | ||||
| 		"radius_endpoint_other": "Points de terminaison du rayon" | ||||
| 	}, | ||||
| 	"operator": { | ||||
| 		"delete_explanation": "Voulez-vous vraiment supprimer cet opérateur ? Cette opération n'est pas réversible", | ||||
| 		"delete_operator": "Supprimer l'opérateur", | ||||
| @@ -970,6 +981,27 @@ | ||||
| 		"title": "Restrictions", | ||||
| 		"tty": "Accès ATS" | ||||
| 	}, | ||||
| 	"roaming": { | ||||
| 		"account_created": "Nouveau compte créé !", | ||||
| 		"account_deleted": "Compte supprimé !", | ||||
| 		"account_one": "Compte", | ||||
| 		"account_other": "Comptes", | ||||
| 		"certificate_deleted": "Certificat supprimé !", | ||||
| 		"certificate_one": "Certificat", | ||||
| 		"certificate_other": "Certificats", | ||||
| 		"city": "Ville", | ||||
| 		"common_name": "Nom commun", | ||||
| 		"country": "Pays", | ||||
| 		"global_reach": "Portée mondiale", | ||||
| 		"global_reach_account_id": "ID de compte ", | ||||
| 		"invalid_certificate": "certificat invalide", | ||||
| 		"invalid_key": "Clé privée invalide", | ||||
| 		"location_details_title": "Emplacement", | ||||
| 		"organization": "Organisation", | ||||
| 		"private_key": "Clé privée", | ||||
| 		"province": "province", | ||||
| 		"state": "Etat" | ||||
| 	}, | ||||
| 	"rrm": { | ||||
| 		"algorithm": "Algorithme", | ||||
| 		"algorithm_other": "Algorithmes", | ||||
| @@ -1091,6 +1123,7 @@ | ||||
| 		"title": "Les abonnés" | ||||
| 	}, | ||||
| 	"system": { | ||||
| 		"advanced": "Avancée", | ||||
| 		"backend_logs": "Journaux principaux", | ||||
| 		"configuration": "Configuration", | ||||
| 		"could_not_retrieve": "Erreur : impossible de récupérer les informations système {{name}} ", | ||||
|   | ||||
| @@ -269,6 +269,7 @@ | ||||
| 		"map": "Mapa", | ||||
| 		"max": "máximo", | ||||
| 		"min": "minuto", | ||||
| 		"miscellaneous": "Diversos", | ||||
| 		"mode": "Modo", | ||||
| 		"model": "Modelo", | ||||
| 		"modified": "Modificado", | ||||
| @@ -737,6 +738,7 @@ | ||||
| 	"form": { | ||||
| 		"captive_web_root_explanation": "Por favor, use apenas arquivos .tar (sem arquivos compactados como .targz, por exemplo)", | ||||
| 		"certificate_file_explanation": "Use um arquivo .pem que comece com \"-----BEGIN CERTIFICATE-----\" e termine com \"-----END CERTIFICATE-----\"", | ||||
| 		"invalid_alphanumeric_with_dash": "Caracteres aceitos. são apenas alfanuméricos (letras e números)", | ||||
| 		"invalid_cidr": "Endereço CIDR IPv4 inválido. Exemplo: 192.168.0.1/12", | ||||
| 		"invalid_email": "E-mail inválido", | ||||
| 		"invalid_file_content": "Conteúdo de arquivo inválido. Confirme se está no formato válido", | ||||
| @@ -763,7 +765,11 @@ | ||||
| 		"invalid_static_ipv4_e": "Endereço inválido, este intervalo é reservado para experimentos (classe E). O primeiro octeto deve ser 223 ou inferior", | ||||
| 		"invalid_third_party": "String JSON de terceiros inválida. Confirme se seu valor é um JSON válido", | ||||
| 		"key_file_explanation": "Use um arquivo .pem que comece com \"-----BEGIN PRIVATE KEY-----\" e termine com \"-----END PRIVATE KEY-----\"", | ||||
| 		"max_length": "Comprimento máximo de {{max}} caracteres.", | ||||
| 		"max_value": "Valor máximo de {{max}}", | ||||
| 		"min_length": "Comprimento mínimo de {{min}} caracteres.", | ||||
| 		"min_max_string": "O valor precisa ter um comprimento entre {{min}} (inclusive) e {{max}} (inclusive)", | ||||
| 		"min_value": "Valor mínimo de {{min}}", | ||||
| 		"missing_interface_upstream": "Você precisa ter pelo menos uma interface upstream. No momento, todas as suas interfaces estão downstream", | ||||
| 		"new_email_to_notify": "Novo e-mail para notificar", | ||||
| 		"new_phone_to_notify": "Novo telefone para notificar", | ||||
| @@ -905,6 +911,11 @@ | ||||
| 		"one": "Notificação", | ||||
| 		"other": "Notificações" | ||||
| 	}, | ||||
| 	"openroaming": { | ||||
| 		"pool_strategy": "Estratégia de pool", | ||||
| 		"radius_endpoint_one": "Ponto final do raio", | ||||
| 		"radius_endpoint_other": "Pontos finais de raio" | ||||
| 	}, | ||||
| 	"operator": { | ||||
| 		"delete_explanation": "Tem certeza de que deseja excluir este operador? Esta operação não é reversível", | ||||
| 		"delete_operator": "Excluir operador", | ||||
| @@ -970,6 +981,27 @@ | ||||
| 		"title": "RESTRIÇÕES", | ||||
| 		"tty": "Acesso TTY" | ||||
| 	}, | ||||
| 	"roaming": { | ||||
| 		"account_created": "Nova conta criada!", | ||||
| 		"account_deleted": "Conta excluída!", | ||||
| 		"account_one": "Conta", | ||||
| 		"account_other": "Contas", | ||||
| 		"certificate_deleted": "Certificado excluído!", | ||||
| 		"certificate_one": "Certificado", | ||||
| 		"certificate_other": "Certificados", | ||||
| 		"city": "Cidade", | ||||
| 		"common_name": "Nome comum", | ||||
| 		"country": "País", | ||||
| 		"global_reach": "Alcance global", | ||||
| 		"global_reach_account_id": "ID da conta", | ||||
| 		"invalid_certificate": "Certificado inválido", | ||||
| 		"invalid_key": "Chave privada inválida", | ||||
| 		"location_details_title": "Localização", | ||||
| 		"organization": "Organização", | ||||
| 		"private_key": "Chave privada", | ||||
| 		"province": "província", | ||||
| 		"state": "Estado" | ||||
| 	}, | ||||
| 	"rrm": { | ||||
| 		"algorithm": "Algoritmo", | ||||
| 		"algorithm_other": "Algoritmos", | ||||
| @@ -1091,6 +1123,7 @@ | ||||
| 		"title": "Inscritos" | ||||
| 	}, | ||||
| 	"system": { | ||||
| 		"advanced": "Avançado", | ||||
| 		"backend_logs": "Registros de back-end", | ||||
| 		"configuration": "Configuração", | ||||
| 		"could_not_retrieve": "Erro: não foi possível recuperar {{name}} informações do sistema", | ||||
|   | ||||
							
								
								
									
										173
									
								
								src/hooks/Network/Simulations.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/hooks/Network/Simulations.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| import { QueryFunctionContext, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | ||||
| import { axiosGw, axiosOwls } from 'constants/axiosInstances'; | ||||
| import { AtLeast } from 'models/General'; | ||||
|  | ||||
| export type Simulation = { | ||||
|   clientInterval: number; | ||||
|   concurrentDeviceS: number; | ||||
|   deviceType: string; | ||||
|   devices: number; | ||||
|   gateway: string; | ||||
|   healthCheckInterval: number; | ||||
|   id: string; | ||||
|   keepAlive: number; | ||||
|   key: string; | ||||
|   macPrefix: string; | ||||
|   minAssociations: number; | ||||
|   maxAssociations: number; | ||||
|   minClients: number; | ||||
|   maxClients: number; | ||||
|   name: string; | ||||
|   reconnectionInterval: number; | ||||
|   simulationLength: number; | ||||
|   stateInterval: number; | ||||
|   threads: number; | ||||
| }; | ||||
|  | ||||
| const getSimulations = () => async () => | ||||
|   axiosOwls.get(`simulation/*`).then((response) => response.data as { list: Simulation[] }); | ||||
|  | ||||
| export const useGetSimulations = () => | ||||
|   useQuery(['simulations'], getSimulations(), { | ||||
|     keepPreviousData: true, | ||||
|     staleTime: 30000, | ||||
|   }); | ||||
|  | ||||
| const getSimulation = (id?: string) => async () => | ||||
|   axiosOwls.get(`simulation/${id}`).then((response) => response.data as { list: Simulation[] }); | ||||
| export const useGetSimulation = ({ id }: { id?: string }) => | ||||
|   useQuery(['simulation', id], getSimulation(id), { | ||||
|     keepPreviousData: true, | ||||
|     enabled: id !== undefined, | ||||
|     staleTime: 30000, | ||||
|   }); | ||||
|  | ||||
| const createSimulation = async (newSimulation: Partial<Simulation>) => axiosOwls.post(`simulation/0`, newSimulation); | ||||
| export const useCreateSimulation = () => { | ||||
|   const queryClient = useQueryClient(); | ||||
|  | ||||
|   return useMutation(createSimulation, { | ||||
|     onSuccess: () => { | ||||
|       queryClient.invalidateQueries(['simulations']); | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const updateSimulation = async (newSimulation: AtLeast<Simulation, 'id'>) => | ||||
|   axiosOwls.put(`simulation/${newSimulation.id}`, newSimulation).then((response) => response.data as Simulation); | ||||
| export const useUpdateSimulation = () => { | ||||
|   const queryClient = useQueryClient(); | ||||
|  | ||||
|   return useMutation(updateSimulation, { | ||||
|     onSuccess: (newSimulation) => { | ||||
|       queryClient.setQueryData(['simulation'], newSimulation); | ||||
|       queryClient.invalidateQueries(['simulations']); | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const deleteSimulation = async ({ id }: { id: string }) => axiosOwls.delete(`simulation/${id}`); | ||||
| export const useDeleteSimulation = () => { | ||||
|   const queryClient = useQueryClient(); | ||||
|  | ||||
|   return useMutation(deleteSimulation, { | ||||
|     onSuccess: () => { | ||||
|       queryClient.invalidateQueries(['simulations']); | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const startSimulation = async ({ id }: { id: string }) => axiosOwls.post(`operation/${id}?operation=start`); | ||||
| export const useStartSimulation = () => { | ||||
|   const queryClient = useQueryClient(); | ||||
|  | ||||
|   return useMutation(startSimulation, { | ||||
|     onSuccess: () => { | ||||
|       queryClient.invalidateQueries(['simulations', 'status']); | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
| const stopSimulation = async ({ runId, simulationId }: { simulationId: string; runId: string }) => | ||||
|   axiosOwls.post(`operation/${simulationId}?runningId=${runId}&operation=stop`); | ||||
| export const useStopSimulation = () => { | ||||
|   const queryClient = useQueryClient(); | ||||
|  | ||||
|   return useMutation(stopSimulation, { | ||||
|     onSuccess: () => { | ||||
|       queryClient.invalidateQueries(['simulations', 'status']); | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
| const cancelSimulation = async ({ runId, simulationId }: { simulationId: string; runId: string }) => | ||||
|   axiosOwls.post(`operation/${simulationId}?runningId=${runId}&operation=cancel`); | ||||
| export const useCancelSimulation = () => { | ||||
|   const queryClient = useQueryClient(); | ||||
|  | ||||
|   return useMutation(cancelSimulation, { | ||||
|     onSuccess: () => { | ||||
|       queryClient.invalidateQueries(['simulations', 'status']); | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| export type SimulationStatus = { | ||||
|   endTime: number; | ||||
|   errorDevices: number; | ||||
|   id: string; | ||||
|   liveDevices: number; | ||||
|   msgsRx: number; | ||||
|   msgsTx: number; | ||||
|   owner: string; | ||||
|   rx: number; | ||||
|   simulationId: string; | ||||
|   startTime: number; | ||||
|   state: 'running' | 'completed' | 'cancelled' | 'none'; | ||||
|   timeToFullDevices: number; | ||||
|   tx: number; | ||||
| }; | ||||
| const getSimulationsStatus = async () => | ||||
|   axiosOwls.get(`status/*`).then((response) => response.data as SimulationStatus[]); | ||||
| export const useGetSimulationsStatus = () => | ||||
|   useQuery(['simulations', 'status'], getSimulationsStatus, { | ||||
|     keepPreviousData: true, | ||||
|     staleTime: Infinity, | ||||
|   }); | ||||
|  | ||||
| const getSimulationStatus = async (context: QueryFunctionContext<[string, string, string]>) => | ||||
|   axiosOwls.get(`status/${context.queryKey[2]}`).then((response) => response.data as SimulationStatus); | ||||
| export const useGetSimulationStatus = ({ id }: { id: string }) => | ||||
|   useQuery(['simulations', 'status', id], getSimulationStatus, { | ||||
|     keepPreviousData: true, | ||||
|     staleTime: Infinity, | ||||
|   }); | ||||
|  | ||||
| const getSimulationHistory = async (context: QueryFunctionContext<[string, string, string]>) => | ||||
|   axiosOwls.get(`results/${context.queryKey[2]}`).then((response) => response.data.list as SimulationStatus[]); | ||||
| export const useGetSimulationHistory = ({ id }: { id: string }) => | ||||
|   useQuery(['simulations', 'history', id], getSimulationHistory, { | ||||
|     keepPreviousData: true, | ||||
|     enabled: !!id, | ||||
|   }); | ||||
|  | ||||
| const deleteSimulationResult = async ({ id }: { id: string }) => axiosOwls.delete(`results/${id}`); | ||||
| export const useDeleteSimulationResult = () => { | ||||
|   const queryClient = useQueryClient(); | ||||
|  | ||||
|   return useMutation(deleteSimulationResult, { | ||||
|     onSuccess: () => { | ||||
|       queryClient.invalidateQueries(['simulations', 'history']); | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const deleteSimulatedDevices = async () => axiosGw.delete('devices?simulatedDevices=true'); | ||||
|  | ||||
| export const useDeleteSimulatedDevices = () => { | ||||
|   const queryClient = useQueryClient(); | ||||
|  | ||||
|   return useMutation(deleteSimulatedDevices, { | ||||
|     onSuccess: () => { | ||||
|       queryClient.invalidateQueries(['devices']); | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
							
								
								
									
										80
									
								
								src/hooks/useNotification.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/hooks/useNotification.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| import * as React from 'react'; | ||||
| import { useToast } from '@chakra-ui/react'; | ||||
| import { useTranslation } from 'react-i18next'; | ||||
| import { v4 as uuid } from 'uuid'; | ||||
| import { isApiError } from 'models/Axios'; | ||||
|  | ||||
| export type SuccessNotificationProps = { | ||||
|   description: string; | ||||
|   id?: string; | ||||
| }; | ||||
|  | ||||
| export type ApiErrorNotificationProps = { | ||||
|   e: unknown; | ||||
|   fallbackMessage?: string; | ||||
|   id?: string; | ||||
| }; | ||||
|  | ||||
| export const useNotification = () => { | ||||
|   const { t } = useTranslation(); | ||||
|   const toast = useToast(); | ||||
|  | ||||
|   const successToast = ({ description, id }: SuccessNotificationProps) => { | ||||
|     toast({ | ||||
|       id: id ?? uuid(), | ||||
|       title: t('common.success'), | ||||
|       description, | ||||
|       status: 'success', | ||||
|       duration: 3000, | ||||
|       isClosable: true, | ||||
|       position: 'top-right', | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   const apiErrorToast = ({ e, id, fallbackMessage }: ApiErrorNotificationProps) => { | ||||
|     if (isApiError(e)) { | ||||
|       toast({ | ||||
|         id: id ?? uuid(), | ||||
|         title: t('common.error'), | ||||
|         description: e.response?.data.ErrorDescription, | ||||
|         status: 'error', | ||||
|         duration: 5000, | ||||
|         isClosable: true, | ||||
|         position: 'top-right', | ||||
|       }); | ||||
|     } else { | ||||
|       toast({ | ||||
|         id: id ?? uuid(), | ||||
|         title: t('common.error'), | ||||
|         description: fallbackMessage, | ||||
|         status: 'error', | ||||
|         duration: 5000, | ||||
|         isClosable: true, | ||||
|         position: 'top-right', | ||||
|       }); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const errorToast = ({ description, id }: SuccessNotificationProps) => { | ||||
|     toast({ | ||||
|       id: id ?? uuid(), | ||||
|       title: t('common.error'), | ||||
|       description, | ||||
|       status: 'error', | ||||
|       duration: 5000, | ||||
|       isClosable: true, | ||||
|       position: 'top-right', | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   return React.useMemo( | ||||
|     () => ({ | ||||
|       successToast, | ||||
|       errorToast, | ||||
|       apiErrorToast, | ||||
|     }), | ||||
|     [t], | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export type UseNotificationReturn = ReturnType<typeof useNotification>; | ||||
| @@ -1,3 +1,6 @@ | ||||
| import { AxiosError as Err } from 'axios'; | ||||
| import { AxiosError as Err, isAxiosError } from 'axios'; | ||||
|  | ||||
| export type AxiosError = Err<{ ErrorDescription: string; ErrorCode: number }>; | ||||
|  | ||||
| export const isApiError = (e: unknown): e is AxiosError => | ||||
|   isAxiosError(e) && (e as AxiosError).response?.data?.ErrorDescription !== undefined; | ||||
|   | ||||
							
								
								
									
										93
									
								
								src/pages/AdvancedSystemPage/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/pages/AdvancedSystemPage/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| import * as React from 'react'; | ||||
| import { | ||||
|   Box, | ||||
|   Button, | ||||
|   Center, | ||||
|   Heading, | ||||
|   Popover, | ||||
|   PopoverArrow, | ||||
|   PopoverBody, | ||||
|   PopoverCloseButton, | ||||
|   PopoverContent, | ||||
|   PopoverHeader, | ||||
|   PopoverTrigger, | ||||
|   Text, | ||||
| } from '@chakra-ui/react'; | ||||
| import { Trash } from '@phosphor-icons/react'; | ||||
| import { Card } from 'components/Containers/Card'; | ||||
| import { CardHeader } from 'components/Containers/Card/CardHeader'; | ||||
| import { CardBody } from 'components/Containers/Card/CardBody'; | ||||
| import { DeleteButton } from 'components/Buttons/DeleteButton'; | ||||
| import { useNotification } from 'hooks/useNotification'; | ||||
| import { useDeleteSimulatedDevices } from 'hooks/Network/Simulations'; | ||||
|  | ||||
| const AdvancedSystemPage = () => { | ||||
|   const { successToast, apiErrorToast } = useNotification(); | ||||
|   const deleteSimulatedDevices = useDeleteSimulatedDevices(); | ||||
|  | ||||
|   const handleDeleteSimulatedDevices = async () => | ||||
|     deleteSimulatedDevices.mutateAsync(undefined, { | ||||
|       onSuccess: () => { | ||||
|         successToast({ | ||||
|           id: 'delete-simulated-devices', | ||||
|           description: 'Simulated devices deleted!', | ||||
|         }); | ||||
|       }, | ||||
|       onError: (e) => { | ||||
|         apiErrorToast({ | ||||
|           id: 'delete-simulated-devices', | ||||
|           e, | ||||
|           fallbackMessage: 'Error deleting simulated devices', | ||||
|         }); | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|   return ( | ||||
|     <Card> | ||||
|       <CardHeader> | ||||
|         <Heading size="md">Operations</Heading> | ||||
|       </CardHeader> | ||||
|       <CardBody> | ||||
|         <Box> | ||||
|           <Heading size="sm">Delete Simulated Devices</Heading> | ||||
|           <Text fontStyle="italic">Delete all simulated devices from the database. This action cannot be undone.</Text> | ||||
|           <Popover> | ||||
|             {({ onClose }) => ( | ||||
|               <> | ||||
|                 <PopoverTrigger> | ||||
|                   <Button colorScheme="red" rightIcon={<Trash size={20} />}> | ||||
|                     Delete | ||||
|                   </Button> | ||||
|                 </PopoverTrigger> | ||||
|                 <PopoverContent> | ||||
|                   <PopoverArrow /> | ||||
|                   <PopoverCloseButton /> | ||||
|                   <PopoverHeader>Confirm</PopoverHeader> | ||||
|                   <PopoverBody> | ||||
|                     <Text>Are you sure you want to delete all simulated devices?</Text> | ||||
|                     <Center mt={4}> | ||||
|                       <Button onClick={onClose} mr={1}> | ||||
|                         Cancel | ||||
|                       </Button> | ||||
|                       <DeleteButton | ||||
|                         ml={1} | ||||
|                         isLoading={deleteSimulatedDevices.isLoading} | ||||
|                         onClick={async () => { | ||||
|                           await handleDeleteSimulatedDevices(); | ||||
|                           onClose(); | ||||
|                         }} | ||||
|                         isCompact={false} | ||||
|                       /> | ||||
|                     </Center> | ||||
|                   </PopoverBody> | ||||
|                 </PopoverContent> | ||||
|               </> | ||||
|             )} | ||||
|           </Popover> | ||||
|         </Box> | ||||
|       </CardBody> | ||||
|     </Card> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export default AdvancedSystemPage; | ||||
| @@ -2,6 +2,7 @@ import React from 'react'; | ||||
| import { Barcode, FloppyDisk, Info, ListBullets, TerminalWindow, UsersThree, WifiHigh } from '@phosphor-icons/react'; | ||||
| import { Route } from 'models/Routes'; | ||||
|  | ||||
| const AdvancedSystemPage = React.lazy(() => import('pages/AdvancedSystemPage')); | ||||
| const DefaultConfigurationsPage = React.lazy(() => import('pages/DefaultConfigurations')); | ||||
| const DefaultFirmwarePage = React.lazy(() => import('pages/DefaultFirmware')); | ||||
| const DevicePage = React.lazy(() => import('pages/Device')); | ||||
| @@ -178,6 +179,13 @@ const routes: Route[] = [ | ||||
|     name: 'system.title', | ||||
|     icon: () => <Info size={28} weight="bold" />, | ||||
|     children: [ | ||||
|       { | ||||
|         id: 'system-advanced', | ||||
|         authorized: ['root', 'partner', 'admin', 'csr', 'system'], | ||||
|         path: '/systemAdvanced', | ||||
|         name: 'system.advanced', | ||||
|         component: AdvancedSystemPage, | ||||
|       }, | ||||
|       { | ||||
|         id: 'system-configuration', | ||||
|         authorized: ['root', 'partner', 'admin', 'csr', 'system'], | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Charles
					Charles