[WIFI-12286] Add support for new venue upgrade API

Signed-off-by: Charles <charles.bourque96@gmail.com>
This commit is contained in:
Charles
2023-02-10 10:26:41 +01:00
parent 84cadb6876
commit f447005817
10 changed files with 341 additions and 72 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "wlan-cloud-owprov-ui", "name": "wlan-cloud-owprov-ui",
"version": "2.9.0(8)", "version": "2.9.0(10)",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "wlan-cloud-owprov-ui", "name": "wlan-cloud-owprov-ui",
"version": "2.9.0(8)", "version": "2.9.0(10)",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@chakra-ui/icons": "^2.0.11", "@chakra-ui/icons": "^2.0.11",

View File

@@ -1,6 +1,6 @@
{ {
"name": "wlan-cloud-owprov-ui", "name": "wlan-cloud-owprov-ui",
"version": "2.9.0(8)", "version": "2.9.0(10)",
"description": "", "description": "",
"main": "index.tsx", "main": "index.tsx",
"scripts": { "scripts": {

View File

@@ -630,6 +630,8 @@
"one": "Gerät", "one": "Gerät",
"reassign_already_owned": "Geräte neu zuweisen, die bereits vorhanden sind und einem anderen Unternehmen/Veranstaltungsort/Abonnenten gehören?", "reassign_already_owned": "Geräte neu zuweisen, die bereits vorhanden sind und einem anderen Unternehmen/Veranstaltungsort/Abonnenten gehören?",
"restricted": "Beschränkt", "restricted": "Beschränkt",
"restricted_overriden": "Dies ist ein eingeschränktes Gerät, aber es befindet sich im Entwicklungsmodus. Alle Einschränkungen werden derzeit ignoriert",
"restrictions_overriden_title": "Dev-Modus",
"sanity": "Gesundheit", "sanity": "Gesundheit",
"start_import": "Geräteimport starten", "start_import": "Geräteimport starten",
"test_batch": "Testen Sie Importdaten", "test_batch": "Testen Sie Importdaten",
@@ -683,6 +685,13 @@
"update_success": "Entität aktualisiert!", "update_success": "Entität aktualisiert!",
"venues_under_root": "Veranstaltungsorte können nicht direkt unter der Root-Entität erstellt werden" "venues_under_root": "Veranstaltungsorte können nicht direkt unter der Root-Entität erstellt werden"
}, },
"firmware": {
"db_update_warning": "Dieser Vorgang wird täglich automatisch durchgeführt, ohne dass dieses manuelle Update verwendet werden muss. Die Aktualisierung dieser Datenbank kann bis zu 25 Minuten dauern",
"last_db_update_modal": "Firmware-Datenbank",
"last_db_update_title": "Datenbank",
"start_db_update": "Datenbankaktualisierung starten",
"started_db_update": "Datenbankaktualisierung gestartet, dieser Vorgang sollte bis zu 25 Minuten dauern"
},
"footer": { "footer": {
"powered_by": "Unterstützt von", "powered_by": "Unterstützt von",
"version": "Ausführung" "version": "Ausführung"
@@ -1123,9 +1132,10 @@
"title": "Veranstaltungsorte", "title": "Veranstaltungsorte",
"update_all_devices": "Alle Gerätekonfigurationen aktualisieren", "update_all_devices": "Alle Gerätekonfigurationen aktualisieren",
"update_success": "Veranstaltungsort aktualisiert!", "update_success": "Veranstaltungsort aktualisiert!",
"upgrade_all_devices": "Aktualisieren Sie alle Geräte auf die neueste Firmware", "upgrade_all_devices": "Aktualisieren Sie die Firmware aller Geräte",
"upgrade_all_devices_error": "Fehler beim Aktualisieren von Geräten: {{e}}", "upgrade_all_devices_error": "Fehler beim Aktualisieren von Geräten: {{e}}",
"upgrade_all_devices_success": "Upgrade von Geräten erfolgreich gestartet!", "upgrade_all_devices_success": "Upgrade von Geräten erfolgreich gestartet!",
"upgrade_options_available": "Hier sind alle verfügbaren Revisionen, bitte wählen Sie diejenige aus, auf die ALLE Geräte dieses Veranstaltungsortes aktualisiert werden sollen",
"use_existing": "Benutze existierendes", "use_existing": "Benutze existierendes",
"use_existing_contacts": "Verwenden Sie vorhandene Kontakte", "use_existing_contacts": "Verwenden Sie vorhandene Kontakte",
"use_this_contact": "Verwenden Sie diesen Kontakt" "use_this_contact": "Verwenden Sie diesen Kontakt"

View File

@@ -630,6 +630,8 @@
"one": "Device", "one": "Device",
"reassign_already_owned": "Reassign devices which already exist and are owned by another entity/venue/subscriber?", "reassign_already_owned": "Reassign devices which already exist and are owned by another entity/venue/subscriber?",
"restricted": "Restricted", "restricted": "Restricted",
"restricted_overriden": "This is a restricted device, but it is in development mode. All restrictions are currently ignored",
"restrictions_overriden_title": "Dev Mode",
"sanity": "Sanity", "sanity": "Sanity",
"start_import": "Start Device Importation", "start_import": "Start Device Importation",
"test_batch": "Test Import Data", "test_batch": "Test Import Data",
@@ -683,6 +685,13 @@
"update_success": "Entity updated!", "update_success": "Entity updated!",
"venues_under_root": "Venues cannot be created directly under the root entity" "venues_under_root": "Venues cannot be created directly under the root entity"
}, },
"firmware": {
"db_update_warning": "This operation is done daily automatically without need to use this manual update. Updating this database can take up to 25 minutes",
"last_db_update_modal": "Firmware Database",
"last_db_update_title": "Database",
"start_db_update": "Start Database Update",
"started_db_update": "Started database update, this operation should take up to 25 minutes to complete"
},
"footer": { "footer": {
"powered_by": "Powered By", "powered_by": "Powered By",
"version": "Version" "version": "Version"
@@ -1123,9 +1132,10 @@
"title": "Venues", "title": "Venues",
"update_all_devices": "Update All Device Configurations", "update_all_devices": "Update All Device Configurations",
"update_success": "Venue updated!", "update_success": "Venue updated!",
"upgrade_all_devices": "Upgrade All Devices to Latest Firmware", "upgrade_all_devices": "Upgrade All Devices Firmware",
"upgrade_all_devices_error": "Error upgrading devices: {{e}}", "upgrade_all_devices_error": "Error upgrading devices: {{e}}",
"upgrade_all_devices_success": "Successfully started upgrading devices!", "upgrade_all_devices_success": "Successfully started upgrading devices!",
"upgrade_options_available": "Here are all available revisions, please select the one you want ALL of this venue's devices to be upgrade to",
"use_existing": "Use Existing", "use_existing": "Use Existing",
"use_existing_contacts": "Use Existing Contacts", "use_existing_contacts": "Use Existing Contacts",
"use_this_contact": "Use this contact" "use_this_contact": "Use this contact"

View File

@@ -630,6 +630,8 @@
"one": "Dispositivo", "one": "Dispositivo",
"reassign_already_owned": "¿Reasignar dispositivos que ya existen y son propiedad de otra entidad/lugar/suscriptor?", "reassign_already_owned": "¿Reasignar dispositivos que ya existen y son propiedad de otra entidad/lugar/suscriptor?",
"restricted": "Restringido", "restricted": "Restringido",
"restricted_overriden": "Este es un dispositivo restringido, pero está en modo de desarrollo. Actualmente se ignoran todas las restricciones.",
"restrictions_overriden_title": "MODO DE DESARROLLO",
"sanity": "Cordura", "sanity": "Cordura",
"start_import": "Iniciar la importación de dispositivos", "start_import": "Iniciar la importación de dispositivos",
"test_batch": "Datos de importación de prueba", "test_batch": "Datos de importación de prueba",
@@ -683,6 +685,13 @@
"update_success": "¡Entidad actualizada!", "update_success": "¡Entidad actualizada!",
"venues_under_root": "Los lugares no se pueden crear directamente bajo la entidad raíz" "venues_under_root": "Los lugares no se pueden crear directamente bajo la entidad raíz"
}, },
"firmware": {
"db_update_warning": "Esta operación se realiza automáticamente todos los días de forma automática sin necesidad de utilizar esta actualización manual. La actualización de esta base de datos puede tardar hasta 25 minutos",
"last_db_update_modal": "Base de datos de firmware",
"last_db_update_title": "Base de datos",
"start_db_update": "Iniciar actualización de la base de datos",
"started_db_update": "Actualización de la base de datos iniciada, esta operación debería tardar hasta 25 minutos en completarse"
},
"footer": { "footer": {
"powered_by": "energizado por", "powered_by": "energizado por",
"version": "Versión" "version": "Versión"
@@ -1123,9 +1132,10 @@
"title": "Sedes", "title": "Sedes",
"update_all_devices": "Actualizar todas las configuraciones de dispositivos", "update_all_devices": "Actualizar todas las configuraciones de dispositivos",
"update_success": "Lugar actualizado!", "update_success": "Lugar actualizado!",
"upgrade_all_devices": "Actualice todos los dispositivos al firmware más reciente", "upgrade_all_devices": "Actualizar el firmware de todos los dispositivos",
"upgrade_all_devices_error": "Error al actualizar dispositivos: {{e}}", "upgrade_all_devices_error": "Error al actualizar dispositivos: {{e}}",
"upgrade_all_devices_success": "¡Comenzó con éxito la actualización de dispositivos!", "upgrade_all_devices_success": "¡Comenzó con éxito la actualización de dispositivos!",
"upgrade_options_available": "Aquí están todas las revisiones disponibles, seleccione la que desea que TODOS los dispositivos de este lugar se actualicen",
"use_existing": "Utilizar existente", "use_existing": "Utilizar existente",
"use_existing_contacts": "Usar contactos existentes", "use_existing_contacts": "Usar contactos existentes",
"use_this_contact": "Usa este contacto" "use_this_contact": "Usa este contacto"

View File

@@ -630,6 +630,8 @@
"one": "Dispositif", "one": "Dispositif",
"reassign_already_owned": "Réattribuer des appareils qui existent déjà et qui appartiennent à une autre entité/salle/abonné ?", "reassign_already_owned": "Réattribuer des appareils qui existent déjà et qui appartiennent à une autre entité/salle/abonné ?",
"restricted": "Limité", "restricted": "Limité",
"restricted_overriden": "Il s'agit d'un appareil restreint, mais il est en mode développement. Toutes les restrictions sont actuellement ignorées",
"restrictions_overriden_title": "Mode développement",
"sanity": "Santé mentale", "sanity": "Santé mentale",
"start_import": "Démarrer l'importation de l'appareil", "start_import": "Démarrer l'importation de l'appareil",
"test_batch": "Tester les données d'importation", "test_batch": "Tester les données d'importation",
@@ -683,6 +685,13 @@
"update_success": "Entité mise à jour !", "update_success": "Entité mise à jour !",
"venues_under_root": "Les lieux ne peuvent pas être créés directement sous l'entité racine" "venues_under_root": "Les lieux ne peuvent pas être créés directement sous l'entité racine"
}, },
"firmware": {
"db_update_warning": "Cette opération se fait automatiquement quotidiennement sans avoir besoin d'utiliser cette mise à jour manuelle. La mise à jour de cette base de données peut prendre jusqu'à 25 minutes",
"last_db_update_modal": "Base de données du micrologiciel",
"last_db_update_title": "Base de données",
"start_db_update": "Démarrer la mise à jour de la base de données",
"started_db_update": "Mise à jour de la base de données démarrée, cette opération devrait prendre jusqu'à 25 minutes"
},
"footer": { "footer": {
"powered_by": "Alimenté par", "powered_by": "Alimenté par",
"version": "Version" "version": "Version"
@@ -1123,9 +1132,10 @@
"title": "Les lieux", "title": "Les lieux",
"update_all_devices": "Mettre à jour toutes les configurations de périphérique", "update_all_devices": "Mettre à jour toutes les configurations de périphérique",
"update_success": "Lieu mis à jour !", "update_success": "Lieu mis à jour !",
"upgrade_all_devices": "Mettre à niveau tous les appareils vers le dernier micrologiciel", "upgrade_all_devices": "Mettre à niveau le micrologiciel de tous les appareils",
"upgrade_all_devices_error": "Erreur lors de la mise à jour des appareils : {{e}}", "upgrade_all_devices_error": "Erreur lors de la mise à jour des appareils : {{e}}",
"upgrade_all_devices_success": "La mise à niveau des appareils a démarré avec succès !", "upgrade_all_devices_success": "La mise à niveau des appareils a démarré avec succès !",
"upgrade_options_available": "Voici toutes les révisions disponibles, veuillez sélectionner celle vers laquelle vous souhaitez que TOUS les appareils de ce lieu soient mis à niveau",
"use_existing": "Utiliser l'existant", "use_existing": "Utiliser l'existant",
"use_existing_contacts": "Utiliser les contacts existants", "use_existing_contacts": "Utiliser les contacts existants",
"use_this_contact": "Utilisez ce contact" "use_this_contact": "Utilisez ce contact"

View File

@@ -630,6 +630,8 @@
"one": "Dispositivo", "one": "Dispositivo",
"reassign_already_owned": "Reatribuir dispositivos que já existem e são de propriedade de outra entidade/local/assinante?", "reassign_already_owned": "Reatribuir dispositivos que já existem e são de propriedade de outra entidade/local/assinante?",
"restricted": "Restrito", "restricted": "Restrito",
"restricted_overriden": "Este é um dispositivo restrito, mas está em modo de desenvolvimento. Todas as restrições são atualmente ignoradas",
"restrictions_overriden_title": "Modo de desenvolvedor",
"sanity": "Sanidade", "sanity": "Sanidade",
"start_import": "Iniciar importação de dispositivos", "start_import": "Iniciar importação de dispositivos",
"test_batch": "Dados de importação de teste", "test_batch": "Dados de importação de teste",
@@ -683,6 +685,13 @@
"update_success": "Entidade atualizada!", "update_success": "Entidade atualizada!",
"venues_under_root": "Os locais não podem ser criados diretamente na entidade raiz" "venues_under_root": "Os locais não podem ser criados diretamente na entidade raiz"
}, },
"firmware": {
"db_update_warning": "Esta operação é feita automaticamente diariamente sem necessidade de usar esta atualização manual. A atualização deste banco de dados pode levar até 25 minutos",
"last_db_update_modal": "banco de dados de firmware",
"last_db_update_title": "base de dados",
"start_db_update": "Iniciar atualização do banco de dados",
"started_db_update": "Atualização do banco de dados iniciada, esta operação deve levar até 25 minutos para ser concluída"
},
"footer": { "footer": {
"powered_by": "Distribuído por", "powered_by": "Distribuído por",
"version": "Versão" "version": "Versão"
@@ -1123,9 +1132,10 @@
"title": "Locais", "title": "Locais",
"update_all_devices": "Atualizar todas as configurações do dispositivo", "update_all_devices": "Atualizar todas as configurações do dispositivo",
"update_success": "Local atualizado!", "update_success": "Local atualizado!",
"upgrade_all_devices": "Atualize todos os dispositivos para o firmware mais recente", "upgrade_all_devices": "Atualize o firmware de todos os dispositivos",
"upgrade_all_devices_error": "Erro ao atualizar dispositivos: {{e}}", "upgrade_all_devices_error": "Erro ao atualizar dispositivos: {{e}}",
"upgrade_all_devices_success": "Atualização de dispositivos iniciada com sucesso!", "upgrade_all_devices_success": "Atualização de dispositivos iniciada com sucesso!",
"upgrade_options_available": "Aqui estão todas as revisões disponíveis, selecione aquela para a qual você deseja que TODOS os dispositivos deste local sejam atualizados",
"use_existing": "Usar existente", "use_existing": "Usar existente",
"use_existing_contacts": "Usar contatos existentes", "use_existing_contacts": "Usar contatos existentes",
"use_this_contact": "Use este contato" "use_this_contact": "Use este contato"

View File

@@ -1,22 +1,43 @@
import { useToast } from '@chakra-ui/react'; import { useToast } from '@chakra-ui/react';
import { useMutation, useQuery } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import useDefaultPage from 'hooks/useDefaultPage'; import useDefaultPage from '../useDefaultPage';
import { AxiosError } from 'models/Axios'; import { AxiosError } from 'models/Axios';
import { Venue } from 'models/Venue'; import { DeviceRules } from 'models/Basic';
import { Note } from 'models/Note';
import { axiosProv } from 'utils/axiosInstances'; import { axiosProv } from 'utils/axiosInstances';
export interface VenueApiResponse {
id: string;
name: string;
description: string;
parent: string;
devices: string[];
children: string[];
contacts: string[];
entity: string;
boards: string[];
created: number;
modified: number;
configurations: string[];
notes: Note[];
variables: string[];
location: string;
sourceIP: string[];
deviceRules: DeviceRules;
}
const getVenuesBatch = async (limit: number, offset: number) => const getVenuesBatch = async (limit: number, offset: number) =>
axiosProv axiosProv
.get(`venue?withExtendedInfo=true&offset=${offset}&limit=${limit}`) .get(`venue?withExtendedInfo=true&offset=${offset}&limit=${limit}`)
.then(({ data }) => data.venues as Venue[]); .then(({ data }) => data.venues as VenueApiResponse[]);
const getAllVenues = async () => { const getAllVenues = async () => {
const limit = 500; const limit = 500;
let offset = 0; let offset = 0;
let data: Venue[] = []; let data: VenueApiResponse[] = [];
let lastResponse: Venue[] = []; let lastResponse: VenueApiResponse[] = [];
do { do {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
lastResponse = await getVenuesBatch(limit, offset); lastResponse = await getVenuesBatch(limit, offset);
@@ -81,15 +102,16 @@ export const useGetSelectVenues = ({ select }: { select: string[] }) => {
); );
}; };
export const useGetVenue = ({ id }: { id: string }) => { export const useGetVenue = ({ id }: { id?: string }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const toast = useToast(); const toast = useToast();
const goToDefaultPage = useDefaultPage(); const goToDefaultPage = useDefaultPage();
return useQuery( return useQuery(
['get-venue', id], ['get-venue', id],
() => axiosProv.get(`venue/${id}?withExtendedInfo=true`).then(({ data }) => data), () => axiosProv.get(`venue/${id}?withExtendedInfo=true`).then(({ data }: { data: VenueApiResponse }) => data),
{ {
enabled: id !== undefined && id !== '',
onError: (e: AxiosError) => { onError: (e: AxiosError) => {
if (!toast.isActive('venue-fetching-error')) if (!toast.isActive('venue-fetching-error'))
toast({ toast({
@@ -104,7 +126,7 @@ export const useGetVenue = ({ id }: { id: string }) => {
isClosable: true, isClosable: true,
position: 'top-right', position: 'top-right',
}); });
goToDefaultPage(); if (e.response?.data?.ErrorCode === 404) goToDefaultPage();
}, },
}, },
); );
@@ -115,10 +137,25 @@ export const useCreateVenue = () =>
axiosProv.post(`venue/0${createObjects ? `?createObjects=${JSON.stringify(createObjects)}` : ''}`, params), axiosProv.post(`venue/0${createObjects ? `?createObjects=${JSON.stringify(createObjects)}` : ''}`, params),
); );
export const useUpdateVenue = ({ id }: { id: string }) => export const useUpdateVenue = ({ id }: { id: string }) => {
useMutation(({ params, createObjects }: { params: unknown; createObjects: unknown }) => const queryClient = useQueryClient();
axiosProv.put(`venue/${id}${createObjects ? `?createObjects=${JSON.stringify(createObjects)}` : ''}`, params),
return useMutation(
({ params, createObjects }: { params: Partial<VenueApiResponse>; createObjects?: unknown }) =>
axiosProv
.put(`venue/${id}${createObjects ? `?createObjects=${JSON.stringify(createObjects)}` : ''}`, params)
.then((res: { data: VenueApiResponse }) => res),
{
onSuccess: ({ data }) => {
queryClient.invalidateQueries(['get-entity-tree']);
queryClient.invalidateQueries(['get-venues']);
queryClient.invalidateQueries(['get-all-locations', id]);
queryClient.setQueryData(['get-venue', id], data);
},
},
); );
};
export const useUpdateVenueDevices = ({ id }: { id: string }) => { export const useUpdateVenueDevices = ({ id }: { id: string }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const toast = useToast(); const toast = useToast();
@@ -185,36 +222,38 @@ export const useRebootVenueDevices = ({ id }: { id: string }) => {
}); });
}; };
export const useUpgradeVenueDevices = ({ id }: { id: string }) => { export const useUpgradeVenueDevices = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const toast = useToast(); const toast = useToast();
return useMutation(() => axiosProv.put(`venue/${id}?upgradeAllDevices=true`, {}), { return useMutation(
onSuccess: () => { (data: { id: string; revision: string }) =>
toast({ axiosProv.put(`venue/${data.id}?upgradeAllDevices=true&revision=${data.revision}`, {}),
id: 'venue-upgrade-devices-success', {
title: t('common.success'), onSuccess: () => {
description: t('venues.upgrade_all_devices_success'), toast({
status: 'success', id: 'venue-upgrade-devices-success',
duration: 5000, title: t('common.success'),
isClosable: true, description: t('venues.upgrade_all_devices_success'),
position: 'top-right', status: 'success',
}); duration: 5000,
isClosable: true,
position: 'top-right',
});
},
onError: (e: AxiosError) => {
toast({
id: uuid(),
title: t('common.error'),
description: e?.response?.data?.ErrorDescription,
status: 'error',
duration: 5000,
isClosable: true,
position: 'top-right',
});
},
}, },
onError: (e: AxiosError) => { );
toast({
id: uuid(),
title: t('common.error'),
description: t('crud.upgrade_all_devices_error', {
e: e?.response?.data?.ErrorDescription,
}),
status: 'error',
duration: 5000,
isClosable: true,
position: 'top-right',
});
},
});
}; };
export const useDeleteVenue = () => useMutation((id) => axiosProv.delete(`venue/${id}`)); export const useDeleteVenue = () => useMutation((id) => axiosProv.delete(`venue/${id}`));
@@ -271,3 +310,21 @@ export const useRemoveVenueContact = ({
}, },
); );
}; };
type Release = {
date: number;
revision: string;
};
const getVenueUpgradeAvailableFirmware = (id: string) =>
axiosProv.put(`venue/${id}?upgradeAllDevices=true&revisionsAvailable=true`, {}).then(
(res: {
data: {
releases: Release[];
releasesCandidates: Release[];
developmentReleases: Release[];
};
}) => res.data,
);
export const useGetVenueUpgradeAvailableFirmware = ({ id }: { id: string }) =>
useQuery(['venue', id, 'availableFirmware'], () => getVenueUpgradeAvailableFirmware(id));

View File

@@ -1,24 +1,20 @@
import React from 'react'; import React from 'react';
import { ChevronDownIcon } from '@chakra-ui/icons'; import { IconButton, Menu, MenuButton, MenuItem, MenuList, Tooltip, useDisclosure } from '@chakra-ui/react';
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/react'; import { Wrench } from 'phosphor-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useRebootVenueDevices, useUpdateVenueDevices, useUpgradeVenueDevices } from 'hooks/Network/Venues'; import VenueFirmwareUpgradeModal from './VenueFirmwareUpgradeModal';
import { useRebootVenueDevices, useUpdateVenueDevices } from 'hooks/Network/Venues';
interface Props { type Props = {
venueId: string; venueId: string;
isDisabled: boolean; isDisabled: boolean;
} };
const VenueActions = ( const VenueActions = ({ venueId, isDisabled }: Props) => {
{
venueId,
isDisabled
}: Props
) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isOpen: isUpgradeOpen, onOpen: onOpenUpgrade, onClose: onCloseUpgrade } = useDisclosure();
const { mutateAsync: rebootDevices } = useRebootVenueDevices({ id: venueId }); const { mutateAsync: rebootDevices } = useRebootVenueDevices({ id: venueId });
const updateDevices = useUpdateVenueDevices({ id: venueId }); const updateDevices = useUpdateVenueDevices({ id: venueId });
const upgradeDevices = useUpgradeVenueDevices({ id: venueId });
const handleUpdateClick = () => { const handleUpdateClick = () => {
updateDevices.mutateAsync(); updateDevices.mutateAsync();
@@ -28,21 +24,22 @@ const VenueActions = (
rebootDevices(); rebootDevices();
}; };
const handleUpgradeDevices = () => {
upgradeDevices.mutateAsync();
};
return ( return (
<Menu> <>
<MenuButton as={Button} rightIcon={<ChevronDownIcon />} ml={2} isDisabled={isDisabled}> <Menu>
{t('common.actions')} <Tooltip label={t('common.actions')} hasArrow>
</MenuButton> <MenuButton as={IconButton} icon={<Wrench size={20} />} ml={2} isDisabled={isDisabled}>
<MenuList> {t('common.actions')}
<MenuItem onClick={handleRebootClick}>{t('venues.reboot_all_devices')}</MenuItem> </MenuButton>
<MenuItem onClick={handleUpdateClick}>{t('venues.update_all_devices')}</MenuItem> </Tooltip>
<MenuItem onClick={handleUpgradeDevices}>{t('venues.upgrade_all_devices')}</MenuItem> <MenuList>
</MenuList> <MenuItem onClick={handleRebootClick}>{t('venues.reboot_all_devices')}</MenuItem>
</Menu> <MenuItem onClick={handleUpdateClick}>{t('venues.update_all_devices')}</MenuItem>
<MenuItem onClick={onOpenUpgrade}>{t('venues.upgrade_all_devices')}</MenuItem>
</MenuList>
</Menu>
<VenueFirmwareUpgradeModal isOpen={isUpgradeOpen} onClose={onCloseUpgrade} venueId={venueId} />
</>
); );
}; };

View File

@@ -0,0 +1,165 @@
import * as React from 'react';
import {
Alert,
AlertDescription,
AlertIcon,
Box,
Button,
Center,
ListItem,
Spinner,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
Text,
UnorderedList,
} from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import FormattedDate from 'components/FormattedDate';
import { Modal } from 'components/Modals/Modal';
import { useGetVenueUpgradeAvailableFirmware, useUpgradeVenueDevices } from 'hooks/Network/Venues';
import { AxiosError } from 'models/Axios';
type Props = {
isOpen: boolean;
onClose: () => void;
venueId: string;
};
const VenueFirmwareUpgradeModal = ({ isOpen, onClose, venueId }: Props) => {
const { t } = useTranslation();
const getAvailableFirmware = useGetVenueUpgradeAvailableFirmware({ id: venueId });
const upgrade = useUpgradeVenueDevices();
const [selectedRevision, setSelectedRevision] = React.useState<string>();
const onRevisionSelect = (revision: string) => () => {
setSelectedRevision(revision);
};
const onUpgradeClick = () => {
if (selectedRevision) {
upgrade.mutateAsync(
{ revision: selectedRevision, id: venueId },
{
onSuccess: () => {
setSelectedRevision(undefined);
onClose();
},
},
);
}
};
const listItemStyle = (revision: string) => ({
cursor: 'pointer',
backgroundColor: revision === selectedRevision ? 'gray.200' : 'white',
});
const displayRevision = (release: { date: number; revision: string }) => (
<ListItem key={release.revision} onClick={onRevisionSelect(release.revision)} {...listItemStyle(release.revision)}>
<FormattedDate date={release.date} />
<Text>{release.revision}</Text>
</ListItem>
);
const placeholder = React.useMemo(() => {
if (getAvailableFirmware.isFetching) {
return (
<Center my={6}>
<Spinner size="xl" />
</Center>
);
}
if (getAvailableFirmware.isError) {
return (
<Center my={6}>
<Alert status="error">
<AlertIcon />
<AlertDescription>
{(getAvailableFirmware.error as AxiosError).response?.data?.ErrorDescription}
</AlertDescription>
</Alert>
</Center>
);
}
return null;
}, [getAvailableFirmware]);
React.useEffect(() => {
if (isOpen) {
setSelectedRevision(undefined);
getAvailableFirmware.refetch();
}
}, [isOpen]);
return (
<Modal isOpen={isOpen} onClose={onClose} title={t('venues.upgrade_all_devices')}>
<Box>
{placeholder || !getAvailableFirmware.data ? (
placeholder
) : (
<>
<Text fontWeight="bold">{t('venues.upgrade_options_available')}</Text>
<Center my={2}>
<Button
colorScheme="yellow"
onClick={onUpgradeClick}
isDisabled={!selectedRevision}
isLoading={upgrade.isLoading}
display="block"
>
<Text>{selectedRevision ? `Upgrade to ` : 'Select a revision to upgrade'}</Text>
{selectedRevision ? <Text mt={1}>{selectedRevision}</Text> : null}
</Button>
</Center>
<Tabs>
<TabList>
<Tab>Official Releases</Tab>
<Tab>Release Candidates</Tab>
<Tab>Dev Releases</Tab>
</TabList>
<TabPanels>
<TabPanel>
<Text fontWeight="bold" textDecor="underline">
Official Releases
</Text>
<UnorderedList>
{getAvailableFirmware.data?.releases
.sort((a, b) => b.date - a.date)
.map((release) => displayRevision(release))}
</UnorderedList>
</TabPanel>
<TabPanel>
<Text fontWeight="bold" textDecor="underline">
Release Candidates
</Text>
<UnorderedList>
{getAvailableFirmware.data?.releasesCandidates
.sort((a, b) => b.date - a.date)
.map((release) => displayRevision(release))}
</UnorderedList>
</TabPanel>
<TabPanel>
<Text fontWeight="bold" textDecor="underline">
Dev Releases
</Text>
<UnorderedList>
{getAvailableFirmware.data?.developmentReleases
.sort((a, b) => b.date - a.date)
.map((release) => displayRevision(release))}
</UnorderedList>
</TabPanel>
</TabPanels>
</Tabs>
</>
)}
</Box>
</Modal>
);
};
export default VenueFirmwareUpgradeModal;