@ -5,6 +5,7 @@ import Modal from '@app/components/Common/Modal';
import PageTitle from '@app/components/Common/PageTitle' ;
import PageTitle from '@app/components/Common/PageTitle' ;
import Table from '@app/components/Common/Table' ;
import Table from '@app/components/Common/Table' ;
import Tooltip from '@app/components/Common/Tooltip' ;
import Tooltip from '@app/components/Common/Tooltip' ;
import useDebouncedState from '@app/hooks/useDebouncedState' ;
import { useUpdateQueryParams } from '@app/hooks/useUpdateQueryParams' ;
import { useUpdateQueryParams } from '@app/hooks/useUpdateQueryParams' ;
import globalMessages from '@app/i18n/globalMessages' ;
import globalMessages from '@app/i18n/globalMessages' ;
import Error from '@app/pages/_error' ;
import Error from '@app/pages/_error' ;
@ -17,6 +18,7 @@ import {
FilterIcon ,
FilterIcon ,
PauseIcon ,
PauseIcon ,
PlayIcon ,
PlayIcon ,
SearchIcon ,
} from '@heroicons/react/solid' ;
} from '@heroicons/react/solid' ;
import type {
import type {
LogMessage ,
LogMessage ,
@ -59,6 +61,8 @@ const SettingsLogs = () => {
const { addToast } = useToasts ( ) ;
const { addToast } = useToasts ( ) ;
const [ currentFilter , setCurrentFilter ] = useState < Filter > ( 'debug' ) ;
const [ currentFilter , setCurrentFilter ] = useState < Filter > ( 'debug' ) ;
const [ currentPageSize , setCurrentPageSize ] = useState ( 25 ) ;
const [ currentPageSize , setCurrentPageSize ] = useState ( 25 ) ;
const [ searchFilter , debouncedSearchFilter , setSearchFilter ] =
useDebouncedState ( '' ) ;
const [ refreshInterval , setRefreshInterval ] = useState ( 5000 ) ;
const [ refreshInterval , setRefreshInterval ] = useState ( 5000 ) ;
const [ activeLog , setActiveLog ] = useState < {
const [ activeLog , setActiveLog ] = useState < {
isOpen : boolean ;
isOpen : boolean ;
@ -76,7 +80,9 @@ const SettingsLogs = () => {
const { data , error } = useSWR < LogsResultsResponse > (
const { data , error } = useSWR < LogsResultsResponse > (
` /api/v1/settings/logs?take= ${ currentPageSize } &skip= ${
` /api/v1/settings/logs?take= ${ currentPageSize } &skip= ${
pageIndex * currentPageSize
pageIndex * currentPageSize
} & filter = $ { currentFilter } ` ,
} & filter = $ { currentFilter } $ {
debouncedSearchFilter ? ` &search= ${ debouncedSearchFilter } ` : ''
} ` ,
{
{
refreshInterval : refreshInterval ,
refreshInterval : refreshInterval ,
revalidateOnFocus : false ,
revalidateOnFocus : false ,
@ -118,15 +124,13 @@ const SettingsLogs = () => {
} ) ;
} ) ;
} ;
} ;
if ( ! data && ! error ) {
// check if there's no data and no errors in the table
return < LoadingSpinner / > ;
// so as to show a spinner inside the table and not refresh the whole component
}
if ( ! data && error ) {
if ( ! data ) {
return < Error statusCode = { 500 } / > ;
return < Error statusCode = { 500 } / > ;
}
}
const hasNextPage = data . pageInfo . pages > pageIndex + 1 ;
const hasNextPage = data ? . pageInfo . pages ? ? 0 > pageIndex + 1 ;
const hasPrevPage = pageIndex > 0 ;
const hasPrevPage = pageIndex > 0 ;
return (
return (
@ -245,10 +249,21 @@ const SettingsLogs = () => {
appDataPath : appData ? appData . appDataPath : '/app/config' ,
appDataPath : appData ? appData . appDataPath : '/app/config' ,
} ) }
} ) }
< / p >
< / p >
< div className = "mt-2 flex flex-grow flex-row sm:flex-grow-0 sm:justify-end" >
< div className = "mt-2 flex flex-grow flex-col sm:flex-grow-0 sm:flex-row sm:justify-end" >
< div className = "mb-2 flex flex-grow sm:mb-0 sm:mr-2 md:flex-grow-0" >
< span className = "inline-flex cursor-default items-center rounded-l-md border border-r-0 border-gray-500 bg-gray-800 px-3 text-sm text-gray-100" >
< SearchIcon className = "h-6 w-6" / >
< / span >
< input
type = "text"
className = "rounded-r-only"
value = { searchFilter }
onChange = { ( e ) = > setSearchFilter ( e . target . value as string ) }
/ >
< / div >
< div className = "mb-2 flex flex-1 flex-row justify-between sm:mb-0 sm:flex-none" >
< div className = "mb-2 flex flex-1 flex-row justify-between sm:mb-0 sm:flex-none" >
< Button
< Button
className = "mr-2 w-full flex-grow"
className = "mr-2 flex flex-grow"
buttonType = { refreshInterval ? 'default' : 'primary' }
buttonType = { refreshInterval ? 'default' : 'primary' }
onClick = { ( ) = > toggleLogs ( ) }
onClick = { ( ) = > toggleLogs ( ) }
>
>
@ -259,34 +274,34 @@ const SettingsLogs = () => {
) }
) }
< / span >
< / span >
< / Button >
< / Button >
< / div >
< div className = "flex flex-grow" >
< div className = "mb-2 flex flex-1 sm:mb-0 sm:flex-none ">
< span className = "inline-flex cursor-default items-center rounded-l-md border border-r-0 border-gray-500 bg-gray-800 px-3 text-sm text-gray-100 ">
< span className = "inline-flex cursor-default items-center rounded-l-md border border-r-0 border-gray-500 bg-gray-800 px-3 text-sm text-gray-100" >
< FilterIcon className = "h-6 w-6" / >
< FilterIcon className = "h-6 w-6" / >
< / span >
< / span >
< select
< select
id = "filter"
id = "filter"
name = "filter"
name = "filter"
onChange = { ( e ) = > {
onChange = { ( e ) = > {
setCurrentFilter ( e . target . value as Filter ) ;
setCurrentFilter ( e . target . value as Filter ) ;
router . push ( router . pathname ) ;
router . push ( router . pathname ) ;
} }
} }
value = { currentFilter }
value = { currentFilter }
className = "rounded-r-only"
className = "rounded-r-only"
>
>
< option value ="debug" >
< option value = "debug" >
{ intl . formatMessage ( messages . filterDebug ) }
{intl . formatMessage ( messages . filterDebug ) }
</ option >
< / option >
< option value = "info" >
< option value = "info" >
{ intl . formatMessage ( messages . filterInfo ) }
{intl . formatMessage ( messages . filterInfo ) }
</ option >
< / option >
< option value = "warn" >
< option value = "warn" >
{ intl . formatMessage ( messages . filterWarn ) }
{intl . formatMessage ( messages . filterWarn ) }
</ option >
< / option >
< option value = "error" >
< option value = "error" >
{ intl . formatMessage ( messages . filterError ) }
{intl . formatMessage ( messages . filterError ) }
</ option >
< / option >
< / select >
< / select >
< / div >
< / div >
< / div >
< / div >
< / div >
< Table >
< Table >
@ -300,73 +315,81 @@ const SettingsLogs = () => {
< / tr >
< / tr >
< / thead >
< / thead >
< Table.TBody >
< Table.TBody >
{ data . results . map ( ( row : LogMessage , index : number ) = > {
{ ! data ? (
return (
< tr >
< tr key = { ` log-list- ${ index } ` } >
< Table.TD colSpan = { 5 } noPadding >
< Table.TD className = "text-gray-300" >
< LoadingSpinner / >
{ intl . formatDate ( row . timestamp , {
< / Table.TD >
year : 'numeric' ,
< / tr >
month : 'short' ,
) : (
day : '2-digit' ,
data . results . map ( ( row : LogMessage , index : number ) = > {
hour : 'numeric' ,
return (
minute : 'numeric' ,
< tr key = { ` log-list- ${ index } ` } >
second : 'numeric' ,
< Table.TD className = "text-gray-300" >
} ) }
{ intl . formatDate ( row . timestamp , {
< / Table.TD >
year : 'numeric' ,
< Table.TD className = "text-gray-300" >
month : 'short' ,
< Badge
day : '2-digit' ,
badgeType = {
hour : 'numeric' ,
row . level === 'error'
minute : 'numeric' ,
? 'danger'
second : 'numeric' ,
: row . level === 'warn'
} ) }
? 'warning'
< / Table.TD >
: row . level === 'info'
< Table.TD className = "text-gray-300" >
? 'success'
< Badge
: 'default'
badgeType = {
}
row . level === 'error'
>
? 'danger'
{ row . level . toUpperCase ( ) }
: row . level === 'warn'
< / Badge >
? 'warning'
< / Table.TD >
: row . level === 'info'
< Table.TD className = "text-gray-300" >
? 'success'
{ row . label ? ? '' }
: 'default'
< / Table.TD >
}
< Table.TD className = "text-gray-300" > { row . message } < / Table.TD >
>
< Table.TD className = "-m-1 flex flex-wrap items-center justify-end" >
{ row . level . toUpperCase ( ) }
{ row . data && (
< / Badge >
< / Table.TD >
< Table.TD className = "text-gray-300" >
{ row . label ? ? '' }
< / Table.TD >
< Table.TD className = "text-gray-300" > { row . message } < / Table.TD >
< Table.TD className = "-m-1 flex flex-wrap items-center justify-end" >
{ row . data && (
< Tooltip
content = { intl . formatMessage ( messages . viewdetails ) }
>
< Button
buttonSize = "sm"
buttonType = "primary"
onClick = { ( ) = >
setActiveLog ( { log : row , isOpen : true } )
}
className = "m-1"
>
< DocumentSearchIcon className = "icon-md" / >
< / Button >
< / Tooltip >
) }
< Tooltip
< Tooltip
content = { intl . formatMessage ( messages . viewdetails ) }
content = { intl . formatMessage ( messages . copyToClipboard ) }
>
>
< Button
< Button
buttonType = "primary"
buttonType = "primary"
buttonSize = "sm"
buttonSize = "sm"
onClick = { ( ) = >
onClick = { ( ) = > copyLogString ( row ) }
setActiveLog ( { log : row , isOpen : true } )
}
className = "m-1"
className = "m-1"
>
>
< DocumentSearchIcon className = "icon-md" / >
< ClipboardCopy Icon className = "icon-md" / >
< / Button >
< / Button >
< / Tooltip >
< / Tooltip >
) }
< / Table.TD >
< Tooltip
< / tr >
content = { intl . formatMessage ( messages . copyToClipboard ) }
) ;
>
} )
< Button
) }
buttonType = "primary"
buttonSize = "sm"
onClick = { ( ) = > copyLogString ( row ) }
className = "m-1"
>
< ClipboardCopyIcon className = "icon-md" / >
< / Button >
< / Tooltip >
< / Table.TD >
< / tr >
) ;
} ) }
{ data . results . length === 0 && (
{ data ? . results . length === 0 && (
< tr className = "relative h-24 p-2 text-white" >
< tr className = "relative h-24 p-2 text-white" >
< Table.TD colSpan = { 5 } noPadding >
< Table.TD colSpan = { 5 } noPadding >
< div className = "flex w-screen flex-col items-center justify-center p-6 md:w-full" >
< div className = "flex w-screen flex-col items-center justify-center p-6 md:w-full" >
@ -396,15 +419,15 @@ const SettingsLogs = () => {
>
>
< div className = "hidden lg:flex lg:flex-1" >
< div className = "hidden lg:flex lg:flex-1" >
< p className = "text-sm" >
< p className = "text-sm" >
{ data . results . length > 0 &&
{ ( data ? . results . length ? ? 0 ) > 0 &&
intl . formatMessage ( globalMessages . showingresults , {
intl . formatMessage ( globalMessages . showingresults , {
from : pageIndex * currentPageSize + 1 ,
from : pageIndex * currentPageSize + 1 ,
to :
to :
data . results . length < currentPageSize
data ? . results . length ? ? 0 < currentPageSize
? pageIndex * currentPageSize +
? pageIndex * currentPageSize +
data . results . length
( data ? . results . length ? ? 0 )
: ( pageIndex + 1 ) * currentPageSize ,
: ( pageIndex + 1 ) * currentPageSize ,
total : data .pageInfo.results,
total : data ? .pageInfo.results ? ? 0 ,
strong : ( msg : React.ReactNode ) = > (
strong : ( msg : React.ReactNode ) = > (
< span className = "font-medium" > { msg } < / span >
< span className = "font-medium" > { msg } < / span >
) ,
) ,