mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui.git
synced 2025-10-29 18:02:31 +00:00
Merge pull request #156 from stephb9959/main
[WIFI-12067] Added crash logs to device details page
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "2.9.0(1)",
|
||||
"version": "2.9.0(2)",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ucentral-client",
|
||||
"version": "2.9.0(1)",
|
||||
"version": "2.9.0(2)",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@chakra-ui/icons": "^2.0.11",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "2.9.0(1)",
|
||||
"version": "2.9.0(2)",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"main": "index.tsx",
|
||||
|
||||
@@ -392,6 +392,7 @@
|
||||
"warning_pushes_one": "Warten auf Geräteverbindung: {{count}}",
|
||||
"warning_pushes_other": "Warten auf Geräteverbindung: {{count}}",
|
||||
"weight": "Gewicht",
|
||||
"wifi_bands_max": "Es können nicht mehr als 8 SSIDs dieses WLAN-Band verwenden",
|
||||
"wifi_frames": "WiFi-Frames"
|
||||
},
|
||||
"contacts": {
|
||||
@@ -601,6 +602,7 @@
|
||||
"certificate_expires_in": "Zertifikat läuft ab in",
|
||||
"certificate_expiry": "Zert. Läuft ab in",
|
||||
"connected": "In Verbindung gebracht",
|
||||
"crash_logs": "Absturzprotokolle",
|
||||
"create_errors": "Fehler beim Versuch, Geräte zu erstellen",
|
||||
"create_success": " Geräte erfolgreich erstellt",
|
||||
"current_firmware": "Aktuelle Firmware",
|
||||
@@ -614,6 +616,7 @@
|
||||
"import_device_warning": "Bitte stellen Sie sicher, dass am Anfang oder Ende von Werten keine zusätzlichen Leerzeichen stehen, es sei denn, es handelt sich um einen Teil des gewünschten Werts",
|
||||
"import_explanation": "Für den Massenimport von Geräten müssen Sie eine CSV-Datei mit den folgenden Spalten verwenden: SerialNumber, DeviceType, Name, Description, Note",
|
||||
"invalid_serial_number": "Ungültige Seriennummer (muss 12 HEX-Zeichen lang sein)",
|
||||
"logs_one": "Log",
|
||||
"new_devices": "Neue Geräte",
|
||||
"no_model_image": "Kein Modellbild gefunden",
|
||||
"not_connected": "Nicht verbunden",
|
||||
|
||||
@@ -392,6 +392,7 @@
|
||||
"warning_pushes_one": "Waiting for devices to connect: {{count}}",
|
||||
"warning_pushes_other": "Waiting for devices to connect: {{count}}",
|
||||
"weight": "Weight",
|
||||
"wifi_bands_max": "There cannot be more than 8 SSIDs using this wifi-band",
|
||||
"wifi_frames": "WiFi Frames"
|
||||
},
|
||||
"contacts": {
|
||||
@@ -601,6 +602,7 @@
|
||||
"certificate_expires_in": "Certificate Expiry",
|
||||
"certificate_expiry": "Cert. Expires In",
|
||||
"connected": "Connected",
|
||||
"crash_logs": "Crash Logs",
|
||||
"create_errors": "errors while trying to create devices",
|
||||
"create_success": " devices successfully created",
|
||||
"current_firmware": "Current Firmware",
|
||||
@@ -614,6 +616,7 @@
|
||||
"import_device_warning": "Please make sure there are no extra spaces at the start or end of any values unless it is part of the value desired",
|
||||
"import_explanation": "To bulk import devices, you need to use a CSV file with the following columns: SerialNumber, DeviceType, Name, Description, Note",
|
||||
"invalid_serial_number": "Invalid Serial Number (needs to be 12 HEX chars)",
|
||||
"logs_one": "Log",
|
||||
"new_devices": "new devices",
|
||||
"no_model_image": "No Model Image Found",
|
||||
"not_connected": "Not Connected",
|
||||
|
||||
@@ -392,6 +392,7 @@
|
||||
"warning_pushes_one": "Esperando a que los dispositivos se conecten: {{count}}",
|
||||
"warning_pushes_other": "Esperando a que los dispositivos se conecten: {{count}}",
|
||||
"weight": "Peso",
|
||||
"wifi_bands_max": "No puede haber más de 8 SSID usando esta banda wifi",
|
||||
"wifi_frames": "Marcos WiFi"
|
||||
},
|
||||
"contacts": {
|
||||
@@ -601,6 +602,7 @@
|
||||
"certificate_expires_in": "El certificado caduca en",
|
||||
"certificate_expiry": "Cert. Expira en",
|
||||
"connected": "Conectado",
|
||||
"crash_logs": "Registros de fallas",
|
||||
"create_errors": "errores al intentar crear dispositivos",
|
||||
"create_success": " dispositivos creados con éxito",
|
||||
"current_firmware": "Firmware actual",
|
||||
@@ -614,6 +616,7 @@
|
||||
"import_device_warning": "Asegúrese de que no haya espacios adicionales al principio o al final de ningún valor a menos que sea parte del valor deseado",
|
||||
"import_explanation": "Para importar dispositivos de forma masiva, debe usar un archivo CSV con las siguientes columnas: Número de serie, Tipo de dispositivo, Nombre, Descripción, Nota",
|
||||
"invalid_serial_number": "Número de serie no válido (debe tener 12 caracteres HEX)",
|
||||
"logs_one": "Iniciar sesión",
|
||||
"new_devices": "Nuevos dispositivos",
|
||||
"no_model_image": "No se encontró ninguna imagen de modelo",
|
||||
"not_connected": "No conectado",
|
||||
|
||||
@@ -392,6 +392,7 @@
|
||||
"warning_pushes_one": "En attente de connexion des appareils : {{count}}",
|
||||
"warning_pushes_other": "En attente de connexion des appareils : {{count}}",
|
||||
"weight": "Poids",
|
||||
"wifi_bands_max": "Il ne peut y avoir plus de 8 SSID utilisant cette bande wifi",
|
||||
"wifi_frames": "Cadres Wi-Fi"
|
||||
},
|
||||
"contacts": {
|
||||
@@ -601,6 +602,7 @@
|
||||
"certificate_expires_in": "Le certificat expire dans",
|
||||
"certificate_expiry": "Cert. Expire dans",
|
||||
"connected": "Connecté",
|
||||
"crash_logs": "Journaux des plantages",
|
||||
"create_errors": "erreurs lors de la tentative de création d'appareils",
|
||||
"create_success": " appareils créés avec succès",
|
||||
"current_firmware": "Firmware actuel",
|
||||
@@ -614,6 +616,7 @@
|
||||
"import_device_warning": "Veuillez vous assurer qu'il n'y a pas d'espaces supplémentaires au début ou à la fin des valeurs, sauf si cela fait partie de la valeur souhaitée",
|
||||
"import_explanation": "Pour importer en masse des appareils, vous devez utiliser un fichier CSV avec les colonnes suivantes : SerialNumber, DeviceType, Name, Description, Note",
|
||||
"invalid_serial_number": "Numéro de série non valide (doit être composé de 12 caractères HEX)",
|
||||
"logs_one": "Bûche",
|
||||
"new_devices": "nouveaux appareils",
|
||||
"no_model_image": "Aucune image de modèle trouvée",
|
||||
"not_connected": "Pas connecté",
|
||||
|
||||
@@ -392,6 +392,7 @@
|
||||
"warning_pushes_one": "Aguardando a conexão dos dispositivos: {{count}}",
|
||||
"warning_pushes_other": "Aguardando a conexão dos dispositivos: {{count}}",
|
||||
"weight": "Peso",
|
||||
"wifi_bands_max": "Não pode haver mais de 8 SSIDs usando esta banda wi-fi",
|
||||
"wifi_frames": "Quadros WiFi"
|
||||
},
|
||||
"contacts": {
|
||||
@@ -601,6 +602,7 @@
|
||||
"certificate_expires_in": "Certificado expira em",
|
||||
"certificate_expiry": "Certificado expira em",
|
||||
"connected": "Conectado",
|
||||
"crash_logs": "Registros de falhas",
|
||||
"create_errors": "erros ao tentar criar dispositivos",
|
||||
"create_success": " dispositivos criados com sucesso",
|
||||
"current_firmware": "Firmware atual",
|
||||
@@ -614,6 +616,7 @@
|
||||
"import_device_warning": "Certifique-se de que não há espaços extras no início ou no final de nenhum valor, a menos que faça parte do valor desejado",
|
||||
"import_explanation": "Para importar dispositivos em massa, você precisa usar um arquivo CSV com as seguintes colunas: SerialNumber, DeviceType, Name, Description, Note",
|
||||
"invalid_serial_number": "Número de série inválido (precisa ter 12 caracteres HEX)",
|
||||
"logs_one": "Registro",
|
||||
"new_devices": "novos dispositivos",
|
||||
"no_model_image": "Nenhuma imagem de modelo encontrada",
|
||||
"not_connected": "Não conectado",
|
||||
|
||||
@@ -11,8 +11,10 @@ export type DeviceLog = {
|
||||
severity: number;
|
||||
};
|
||||
|
||||
const getDeviceLogs = (limit: number, serialNumber?: string) => async () =>
|
||||
axiosGw.get(`device/${serialNumber}/logs?newest=true&limit=${limit}`).then((response) => response.data) as Promise<{
|
||||
const getDeviceLogs = (limit: number, serialNumber?: string, logType?: 0 | 1) => async () =>
|
||||
axiosGw
|
||||
.get(`device/${serialNumber}/logs?newest=true&limit=${limit}&logType=${logType}`)
|
||||
.then((response) => response.data) as Promise<{
|
||||
values: DeviceLog[];
|
||||
serialNumber: string;
|
||||
}>;
|
||||
@@ -21,20 +23,29 @@ export const useGetDeviceLogs = ({
|
||||
serialNumber,
|
||||
limit,
|
||||
onError,
|
||||
logType,
|
||||
}: {
|
||||
serialNumber?: string;
|
||||
limit: number;
|
||||
onError?: (e: AxiosError) => void;
|
||||
logType?: 0 | 1;
|
||||
}) =>
|
||||
useQuery(['devicelogs', serialNumber, { limit }], getDeviceLogs(limit, serialNumber), {
|
||||
useQuery(['devicelogs', serialNumber, { limit, logType }], getDeviceLogs(limit, serialNumber, logType ?? 0), {
|
||||
keepPreviousData: true,
|
||||
enabled: serialNumber !== undefined && serialNumber !== '',
|
||||
staleTime: 30000,
|
||||
onError,
|
||||
});
|
||||
|
||||
const deleteLogs = async ({ serialNumber, endDate }: { serialNumber: string; endDate: number }) =>
|
||||
axiosGw.delete(`device/${serialNumber}/logs?endDate=${endDate}`);
|
||||
const deleteLogs = async ({
|
||||
serialNumber,
|
||||
endDate,
|
||||
logType,
|
||||
}: {
|
||||
serialNumber: string;
|
||||
endDate: number;
|
||||
logType: 0 | 1;
|
||||
}) => axiosGw.delete(`device/${serialNumber}/logs?endDate=${endDate}&logType=${logType}`);
|
||||
export const useDeleteLogs = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
@@ -45,46 +56,62 @@ export const useDeleteLogs = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const getLogsBatch = (serialNumber?: string, start?: number, end?: number, limit?: number, offset?: number) =>
|
||||
const getLogsBatch = (
|
||||
serialNumber?: string,
|
||||
start?: number,
|
||||
end?: number,
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
logType?: 0 | 1,
|
||||
) =>
|
||||
axiosGw
|
||||
.get(`device/${serialNumber}/logs?startDate=${start}&endDate=${end}&limit=${limit}&offset=${offset}`)
|
||||
.get(
|
||||
`device/${serialNumber}/logs?startDate=${start}&endDate=${end}&limit=${limit}&offset=${offset}&logType=${logType}`,
|
||||
)
|
||||
.then((response) => response.data) as Promise<{
|
||||
values: DeviceLog[];
|
||||
serialNumber: string;
|
||||
}>;
|
||||
|
||||
const getDeviceLogsWithTimestamps = (serialNumber?: string, start?: number, end?: number) => async () => {
|
||||
let offset = 0;
|
||||
const limit = 100;
|
||||
let logs: DeviceLog[] = [];
|
||||
let latestResponse: {
|
||||
values: DeviceLog[];
|
||||
serialNumber: string;
|
||||
const getDeviceLogsWithTimestamps =
|
||||
(serialNumber?: string, start?: number, end?: number, logType?: 0 | 1) => async () => {
|
||||
let offset = 0;
|
||||
const limit = 100;
|
||||
let logs: DeviceLog[] = [];
|
||||
let latestResponse: {
|
||||
values: DeviceLog[];
|
||||
serialNumber: string;
|
||||
};
|
||||
do {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
latestResponse = await getLogsBatch(serialNumber, start, end, limit, offset, logType);
|
||||
logs = logs.concat(latestResponse.values);
|
||||
offset += limit;
|
||||
} while (latestResponse.values.length === limit);
|
||||
return {
|
||||
values: logs,
|
||||
};
|
||||
};
|
||||
do {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
latestResponse = await getLogsBatch(serialNumber, start, end, limit, offset);
|
||||
logs = logs.concat(latestResponse.values);
|
||||
offset += limit;
|
||||
} while (latestResponse.values.length === limit);
|
||||
return {
|
||||
values: logs,
|
||||
};
|
||||
};
|
||||
|
||||
export const useGetDeviceLogsWithTimestamps = ({
|
||||
serialNumber,
|
||||
start,
|
||||
end,
|
||||
onError,
|
||||
logType,
|
||||
}: {
|
||||
serialNumber?: string;
|
||||
start?: number;
|
||||
end?: number;
|
||||
onError?: (e: AxiosError) => void;
|
||||
logType?: 0 | 1;
|
||||
}) =>
|
||||
useQuery(['devicelogs', serialNumber, { start, end }], getDeviceLogsWithTimestamps(serialNumber, start, end), {
|
||||
enabled: serialNumber !== undefined && serialNumber !== '' && start !== undefined && end !== undefined,
|
||||
staleTime: 1000 * 60,
|
||||
onError,
|
||||
});
|
||||
useQuery(
|
||||
['devicelogs', serialNumber, { start, end, logType }],
|
||||
getDeviceLogsWithTimestamps(serialNumber, start, end, logType ?? 0),
|
||||
{
|
||||
enabled: serialNumber !== undefined && serialNumber !== '' && start !== undefined && end !== undefined,
|
||||
staleTime: 1000 * 60,
|
||||
onError,
|
||||
},
|
||||
);
|
||||
|
||||
94
src/pages/Device/LogsCard/LogHistory/CrashLogs.tsx
Normal file
94
src/pages/Device/LogsCard/LogHistory/CrashLogs.tsx
Normal 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 CrashLogs = ({ 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: 1,
|
||||
});
|
||||
|
||||
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={0} />
|
||||
<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 CrashLogs;
|
||||
@@ -16,8 +16,8 @@ const CustomInputButton = React.forwardRef(
|
||||
),
|
||||
);
|
||||
|
||||
type Props = { serialNumber: string };
|
||||
const DeleteLogModal = ({ serialNumber }: Props) => {
|
||||
type Props = { serialNumber: string; logType: 0 | 1 };
|
||||
const DeleteLogModal = ({ serialNumber, logType }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const toast = useToast();
|
||||
const modalProps = useDisclosure();
|
||||
@@ -26,7 +26,7 @@ const DeleteLogModal = ({ serialNumber }: Props) => {
|
||||
|
||||
const onDeleteClick = () => {
|
||||
deleteLogs.mutate(
|
||||
{ endDate: Math.floor(date.getTime() / 1000), serialNumber },
|
||||
{ endDate: Math.floor(date.getTime() / 1000), serialNumber, logType },
|
||||
{
|
||||
onSuccess: () => {
|
||||
modalProps.onClose();
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import * as React from 'react';
|
||||
import { Box, Button, Code, Heading, useClipboard } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import FormattedDate from 'components/InformationDisplays/FormattedDate';
|
||||
import { Modal } from 'components/Modals/Modal';
|
||||
import { DeviceLog } from 'hooks/Network/DeviceLogs';
|
||||
|
||||
type Props = {
|
||||
modalProps: {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
};
|
||||
log?: DeviceLog;
|
||||
};
|
||||
|
||||
const DetailedLogViewModal = ({ modalProps, log }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { hasCopied, onCopy, setValue } = useClipboard(JSON.stringify(log?.log ?? {}, null, 2));
|
||||
|
||||
React.useEffect(() => {
|
||||
setValue(JSON.stringify(log?.log ?? {}, null, 2));
|
||||
}, [log]);
|
||||
|
||||
if (!log) return null;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={modalProps.isOpen}
|
||||
onClose={modalProps.onClose}
|
||||
title={t('devices.logs_one')}
|
||||
topRightButtons={
|
||||
<Button onClick={onCopy} size="md" colorScheme="teal">
|
||||
{hasCopied ? `${t('common.copied')}!` : t('common.copy')}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Box>
|
||||
<Heading size="sm">
|
||||
<FormattedDate date={log.recorded} />
|
||||
</Heading>
|
||||
<Heading size="sm">
|
||||
{t('controller.devices.severity')}: {log.severity}
|
||||
</Heading>
|
||||
<Heading size="sm">
|
||||
{t('controller.devices.config_id')}: {log.UUID}
|
||||
</Heading>
|
||||
<Code whiteSpace="pre-line" mt={2}>
|
||||
{log.log}
|
||||
</Code>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DetailedLogViewModal;
|
||||
@@ -16,7 +16,7 @@ const LogHistory = ({ serialNumber }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const [limit, setLimit] = React.useState(25);
|
||||
const [hiddenColumns, setHiddenColumns] = React.useState<string[]>([]);
|
||||
const { time, setTime, getCustomLogs, getLogs, columns } = useDeviceLogsTable({ serialNumber, limit });
|
||||
const { time, setTime, getCustomLogs, getLogs, columns } = useDeviceLogsTable({ serialNumber, limit, logType: 0 });
|
||||
|
||||
const setNewTime = (start: Date, end: Date) => {
|
||||
setTime({ start, end });
|
||||
@@ -48,7 +48,7 @@ const LogHistory = ({ serialNumber }: Props) => {
|
||||
setHiddenColumns={setHiddenColumns}
|
||||
preference="gateway.device.logs.hiddenColumns"
|
||||
/>
|
||||
<DeleteLogModal serialNumber={serialNumber} />
|
||||
<DeleteLogModal serialNumber={serialNumber} logType={0} />
|
||||
<RefreshButton isCompact isFetching={getLogs.isFetching} onClick={getLogs.refetch} colorScheme="blue" />
|
||||
</HStack>
|
||||
</Flex>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { Box, IconButton, Text, useDisclosure } from '@chakra-ui/react';
|
||||
import { MagnifyingGlass } from 'phosphor-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import DetailedLogViewModal from './DetailedLogViewModal';
|
||||
import FormattedDate from 'components/InformationDisplays/FormattedDate';
|
||||
import { DeviceLog, useGetDeviceLogs, useGetDeviceLogsWithTimestamps } from 'hooks/Network/DeviceLogs';
|
||||
import { Column } from 'models/Table';
|
||||
@@ -8,18 +10,49 @@ import { Column } from 'models/Table';
|
||||
type Props = {
|
||||
serialNumber: string;
|
||||
limit: number;
|
||||
logType: 0 | 1;
|
||||
};
|
||||
|
||||
const useDeviceLogsTable = ({ serialNumber, limit }: Props) => {
|
||||
const useDeviceLogsTable = ({ serialNumber, limit, logType }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const getLogs = useGetDeviceLogs({ serialNumber, limit });
|
||||
const getLogs = useGetDeviceLogs({ serialNumber, limit, logType });
|
||||
const modalProps = useDisclosure();
|
||||
const [log, setLog] = React.useState<DeviceLog | undefined>();
|
||||
const [time, setTime] = React.useState<{ start: Date; end: Date } | undefined>();
|
||||
const getCustomLogs = useGetDeviceLogsWithTimestamps({
|
||||
serialNumber,
|
||||
start: time ? Math.floor(time.start.getTime() / 1000) : undefined,
|
||||
end: time ? Math.floor(time.end.getTime() / 1000) : undefined,
|
||||
logType,
|
||||
});
|
||||
|
||||
const onOpen = React.useCallback((v: DeviceLog) => {
|
||||
setLog(v);
|
||||
modalProps.onOpen();
|
||||
}, []);
|
||||
|
||||
const logCell = React.useCallback(
|
||||
(v: DeviceLog) =>
|
||||
logType === 1 ? (
|
||||
<Box display="flex">
|
||||
<IconButton
|
||||
aria-label="Open Log Details"
|
||||
onClick={() => onOpen(v)}
|
||||
colorScheme="blue"
|
||||
icon={<MagnifyingGlass size={16} />}
|
||||
size="xs"
|
||||
mr={2}
|
||||
/>
|
||||
<Text my="auto" maxW="calc(20vw)" textOverflow="ellipsis" overflow="hidden" whiteSpace="nowrap">
|
||||
{v.log}
|
||||
</Text>
|
||||
</Box>
|
||||
) : (
|
||||
v.log
|
||||
),
|
||||
[onOpen],
|
||||
);
|
||||
|
||||
const dateCell = React.useCallback(
|
||||
(v: number) => (
|
||||
<Box>
|
||||
@@ -65,6 +98,7 @@ const useDeviceLogsTable = ({ serialNumber, limit }: Props) => {
|
||||
Footer: '',
|
||||
accessor: 'log',
|
||||
customWidth: '35px',
|
||||
Cell: (v) => logCell(v.cell.row.original),
|
||||
disableSortBy: true,
|
||||
},
|
||||
{
|
||||
@@ -85,6 +119,7 @@ const useDeviceLogsTable = ({ serialNumber, limit }: Props) => {
|
||||
getCustomLogs,
|
||||
time,
|
||||
setTime,
|
||||
modal: <DetailedLogViewModal modalProps={modalProps} log={log} />,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import CommandHistory from './CommandHistory';
|
||||
import HealthCheckHistory from './HealthCheckHistory';
|
||||
import LogHistory from './LogHistory';
|
||||
import CrashLogs from './LogHistory/CrashLogs';
|
||||
import { Card } from 'components/Containers/Card';
|
||||
import { CardBody } from 'components/Containers/Card/CardBody';
|
||||
|
||||
@@ -32,6 +33,9 @@ const DeviceLogsCard = ({ serialNumber }: Props) => {
|
||||
<Tab fontSize="lg" fontWeight="bold">
|
||||
{t('controller.devices.logs')}
|
||||
</Tab>
|
||||
<Tab fontSize="lg" fontWeight="bold">
|
||||
{t('devices.crash_logs')}
|
||||
</Tab>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel p={0}>
|
||||
@@ -51,10 +55,12 @@ const DeviceLogsCard = ({ serialNumber }: Props) => {
|
||||
<TabPanel>
|
||||
<HealthCheckHistory serialNumber={serialNumber} />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<LogHistory serialNumber={serialNumber} />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<CrashLogs serialNumber={serialNumber} />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</CardBody>
|
||||
|
||||
Reference in New Issue
Block a user