mirror of
				https://github.com/optim-enterprises-bv/OptimCloud-gw-ui.git
				synced 2025-10-30 17:57:46 +00:00 
			
		
		
		
	[WIFI-12613] Display reboot logs on device 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.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", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "ucentral-client", | ||||
|   "version": "2.10.0(41)", | ||||
|   "version": "2.10.0(42)", | ||||
|   "description": "", | ||||
|   "private": true, | ||||
|   "main": "index.tsx", | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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 }], | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
							
								
								
									
										94
									
								
								src/pages/Device/LogsCard/LogHistory/RebootLogs.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/pages/Device/LogsCard/LogHistory/RebootLogs.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 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; | ||||
| @@ -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) => { | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Charles
					Charles