[WIFI-12613] Display reboot logs on device page

Signed-off-by: Charles <charles.bourque96@gmail.com>
This commit is contained in:
Charles
2023-05-17 10:28:19 +02:00
parent cafb950aa7
commit edcca87acf
13 changed files with 123 additions and 14 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ucentral-client",
"version": "2.10.0(41)",
"version": "2.10.0(42)",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ucentral-client",
"version": "2.10.0(41)",
"version": "2.10.0(42)",
"license": "ISC",
"dependencies": {
"@chakra-ui/icons": "^2.0.18",

View File

@@ -1,6 +1,6 @@
{
"name": "ucentral-client",
"version": "2.10.0(41)",
"version": "2.10.0(42)",
"description": "",
"private": true,
"main": "index.tsx",

View File

@@ -644,6 +644,7 @@
"notifications": "Gerätebenachrichtigungen",
"one": "Gerät",
"reassign_already_owned": "Geräte neu zuweisen, die bereits vorhanden sind und einem anderen Unternehmen/Veranstaltungsort/Abonnenten gehören?",
"reboot_logs": "Neustartprotokolle",
"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",

View File

@@ -644,6 +644,7 @@
"notifications": "Device Notifications",
"one": "Device",
"reassign_already_owned": "Reassign devices which already exist and are owned by another entity/venue/subscriber?",
"reboot_logs": "Reboot Logs",
"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",

View File

@@ -644,6 +644,7 @@
"notifications": "notificaciones de dispositivos",
"one": "Dispositivo",
"reassign_already_owned": "¿Reasignar dispositivos que ya existen y son propiedad de otra entidad/lugar/suscriptor?",
"reboot_logs": "Reiniciar registros",
"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",

View File

@@ -644,6 +644,7 @@
"notifications": "notifications de l'appareil",
"one": "Dispositif",
"reassign_already_owned": "Réattribuer des appareils qui existent déjà et qui appartiennent à une autre entité/salle/abonné ?",
"reboot_logs": "Journaux de redémarrage",
"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",

View File

@@ -644,6 +644,7 @@
"notifications": "Notificações do dispositivo",
"one": "Dispositivo",
"reassign_already_owned": "Reatribuir dispositivos que já existem e são de propriedade de outra entidade/local/assinante?",
"reboot_logs": "Registros de reinicialização",
"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",

View File

@@ -11,7 +11,7 @@ export type DeviceLog = {
severity: number;
};
const getDeviceLogs = (limit: number, serialNumber?: string, logType?: 0 | 1) => async () =>
const getDeviceLogs = (limit: number, serialNumber?: string, logType?: 0 | 1 | 2) => async () =>
axiosGw
.get(`device/${serialNumber}/logs?newest=true&limit=${limit}&logType=${logType}`)
.then((response) => response.data) as Promise<{
@@ -28,7 +28,7 @@ export const useGetDeviceLogs = ({
serialNumber?: string;
limit: number;
onError?: (e: AxiosError) => void;
logType?: 0 | 1;
logType?: 0 | 1 | 2;
}) =>
useQuery(['devicelogs', serialNumber, { limit, logType }], getDeviceLogs(limit, serialNumber, logType ?? 0), {
keepPreviousData: true,
@@ -44,7 +44,7 @@ const deleteLogs = async ({
}: {
serialNumber: string;
endDate: number;
logType: 0 | 1;
logType: 0 | 1 | 2;
}) => axiosGw.delete(`device/${serialNumber}/logs?endDate=${endDate}&logType=${logType}`);
export const useDeleteLogs = () => {
const queryClient = useQueryClient();
@@ -62,7 +62,7 @@ const getLogsBatch = (
end?: number,
limit?: number,
offset?: number,
logType?: 0 | 1,
logType?: 0 | 1 | 2,
) =>
axiosGw
.get(
@@ -74,7 +74,7 @@ const getLogsBatch = (
}>;
const getDeviceLogsWithTimestamps =
(serialNumber?: string, start?: number, end?: number, logType?: 0 | 1) => async () => {
(serialNumber?: string, start?: number, end?: number, logType?: 0 | 1 | 2) => async () => {
let offset = 0;
const limit = 100;
let logs: DeviceLog[] = [];
@@ -104,7 +104,7 @@ export const useGetDeviceLogsWithTimestamps = ({
start?: number;
end?: number;
onError?: (e: AxiosError) => void;
logType?: 0 | 1;
logType?: 0 | 1 | 2;
}) =>
useQuery(
['devicelogs', serialNumber, { start, end, logType }],

View File

@@ -52,7 +52,7 @@ const CrashLogs = ({ serialNumber }: Props) => {
setHiddenColumns={setHiddenColumns}
preference="gateway.device.logs.hiddenColumns"
/>
<DeleteLogModal serialNumber={serialNumber} logType={0} />
<DeleteLogModal serialNumber={serialNumber} logType={1} />
<RefreshButton isCompact isFetching={getLogs.isFetching} onClick={getLogs.refetch} colorScheme="blue" />
</HStack>
</Flex>

View File

@@ -16,7 +16,7 @@ const CustomInputButton = React.forwardRef(
),
);
type Props = { serialNumber: string; logType: 0 | 1 };
type Props = { serialNumber: string; logType: 0 | 1 | 2 };
const DeleteLogModal = ({ serialNumber, logType }: Props) => {
const { t } = useTranslation();
const toast = useToast();

View File

@@ -0,0 +1,94 @@
import * as React from 'react';
import { Box, Button, Center, Flex, Heading, HStack, Spacer } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import HistoryDatePickers from '../DatePickers';
import DeleteLogModal from './DeleteModal';
import useDeviceLogsTable from './useDeviceLogsTable';
import { RefreshButton } from 'components/Buttons/RefreshButton';
import { ColumnPicker } from 'components/DataTables/ColumnPicker';
import { DataTable } from 'components/DataTables/DataTable';
import { Column } from 'models/Table';
type Props = {
serialNumber: string;
};
const RebootLogs = ({ serialNumber }: Props) => {
const { t } = useTranslation();
const [limit, setLimit] = React.useState(25);
const [hiddenColumns, setHiddenColumns] = React.useState<string[]>([]);
const { time, setTime, getCustomLogs, getLogs, columns, modal } = useDeviceLogsTable({
serialNumber,
limit,
logType: 2,
});
const setNewTime = (start: Date, end: Date) => {
setTime({ start, end });
};
const onClear = () => {
setTime(undefined);
};
const raiseLimit = () => {
setLimit(limit + 25);
};
const noMoreAvailable = getLogs.data !== undefined && getLogs.data.values.length < limit;
const data = React.useMemo(() => {
if (getCustomLogs.data) return getCustomLogs.data.values.sort((a, b) => b.recorded - a.recorded);
if (getLogs.data) return getLogs.data.values;
return [];
}, [getLogs.data, getCustomLogs.data]);
return (
<Box>
<Flex>
<Spacer />
<HStack>
<HistoryDatePickers defaults={time} setTime={setNewTime} onClear={onClear} />
<ColumnPicker
columns={columns as Column<unknown>[]}
hiddenColumns={hiddenColumns}
setHiddenColumns={setHiddenColumns}
preference="gateway.device.logs.hiddenColumns"
/>
<DeleteLogModal serialNumber={serialNumber} logType={2} />
<RefreshButton isCompact isFetching={getLogs.isFetching} onClick={getLogs.refetch} colorScheme="blue" />
</HStack>
</Flex>
<Box overflowY="auto" h="300px">
<DataTable
columns={
columns as {
id: string;
Header: string;
Footer: string;
accessor: string;
}[]
}
data={data}
isLoading={getLogs.isFetching || getCustomLogs.isFetching}
hiddenColumns={hiddenColumns}
obj={t('controller.devices.logs')}
// @ts-ignore
hideControls
showAllRows
/>
{getLogs.data !== undefined && (
<Center mt={1} hidden={getCustomLogs.data !== undefined}>
{!noMoreAvailable || getLogs.isFetching ? (
<Button colorScheme="blue" onClick={raiseLimit} isLoading={getLogs.isFetching}>
{t('controller.devices.show_more')}
</Button>
) : (
<Heading size="sm">{t('controller.devices.no_more_available')}!</Heading>
)}
</Center>
)}
</Box>
{modal}
</Box>
);
};
export default RebootLogs;

View File

@@ -10,7 +10,7 @@ import { Column } from 'models/Table';
type Props = {
serialNumber: string;
limit: number;
logType: 0 | 1;
logType: 0 | 1 | 2;
};
const useDeviceLogsTable = ({ serialNumber, limit, logType }: Props) => {

View File

@@ -1,10 +1,11 @@
import * as React from 'react';
import { Box, Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react';
import { Box, Tab, TabList, TabPanel, TabPanels, Tabs, useBreakpoint } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import CommandHistory from './CommandHistory';
import HealthCheckHistory from './HealthCheckHistory';
import LogHistory from './LogHistory';
import CrashLogs from './LogHistory/CrashLogs';
import RebootLogs from './LogHistory/RebootLogs';
import { Card } from 'components/Containers/Card';
import { CardBody } from 'components/Containers/Card/CardBody';
@@ -13,12 +14,15 @@ type Props = {
};
const DeviceLogsCard = ({ serialNumber }: Props) => {
const { t } = useTranslation();
const breakpoint = useBreakpoint();
const [tabIndex, setTabIndex] = React.useState(0);
const handleTabsChange = React.useCallback((index: number) => {
setTabIndex(index);
}, []);
const isCompact = breakpoint === 'base' || breakpoint === 'sm' || breakpoint === 'md' || breakpoint === 'lg';
return (
<Card p={0} mb={4}>
<CardBody p={0}>
@@ -34,7 +38,10 @@ const DeviceLogsCard = ({ serialNumber }: Props) => {
{t('controller.devices.logs')}
</Tab>
<Tab fontSize="lg" fontWeight="bold">
{t('devices.crash_logs')}
{isCompact ? 'Crashes' : t('devices.crash_logs')}
</Tab>
<Tab fontSize="lg" fontWeight="bold">
{isCompact ? 'Reboots' : t('devices.reboot_logs')}
</Tab>
</TabList>
<TabPanels>
@@ -61,6 +68,9 @@ const DeviceLogsCard = ({ serialNumber }: Props) => {
<TabPanel>
<CrashLogs serialNumber={serialNumber} />
</TabPanel>
<TabPanel>
<RebootLogs serialNumber={serialNumber} />
</TabPanel>
</TabPanels>
</Tabs>
</CardBody>