2022-03-30 01:36:59 -05:00
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
2024-01-01 02:43:48 -06:00
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
2022-03-30 01:36:59 -05:00
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
2024-07-03 05:47:29 -05:00
import React , { useEffect , useMemo , useState , Fragment } from 'react' ;
2024-06-06 06:43:12 -05:00
import { styled } from '@mui/material/styles' ;
2022-03-30 01:36:59 -05:00
import gettext from 'sources/gettext' ;
import PropTypes from 'prop-types' ;
import getApiInstance from 'sources/api_instance' ;
import PgTable from 'sources/components/PgTable' ;
2024-07-03 05:47:29 -05:00
import { InputCheckbox , FormInputSwitch , FormInputToggle } from '../../../static/js/components/FormComponents' ;
2022-03-30 01:36:59 -05:00
import url _for from 'sources/url_for' ;
import Graphs from './Graphs' ;
2024-04-08 21:51:14 -05:00
import { Box , Tab , Tabs } from '@mui/material' ;
2022-03-30 01:36:59 -05:00
import { PgIconButton } from '../../../static/js/components/Buttons' ;
2024-04-08 21:51:14 -05:00
import CancelIcon from '@mui/icons-material/Cancel' ;
import StopSharpIcon from '@mui/icons-material/StopSharp' ;
2022-03-30 01:36:59 -05:00
import WelcomeDashboard from './WelcomeDashboard' ;
import ActiveQuery from './ActiveQuery.ui' ;
2024-07-03 05:47:29 -05:00
import ServerLog from './ServerLog.ui' ;
2022-03-30 01:36:59 -05:00
import _ from 'lodash' ;
2022-04-05 01:40:51 -05:00
import EmptyPanelMessage from '../../../static/js/components/EmptyPanelMessage' ;
2022-04-14 00:41:45 -05:00
import TabPanel from '../../../static/js/components/TabPanel' ;
2023-09-28 04:53:15 -05:00
import Summary from './SystemStats/Summary' ;
2024-07-02 00:04:30 -05:00
import CpuDetails from './SystemStats/CpuDetails' ;
2023-09-28 04:53:15 -05:00
import Memory from './SystemStats/Memory' ;
import Storage from './SystemStats/Storage' ;
2023-10-23 07:13:17 -05:00
import withStandardTabInfo from '../../../static/js/helpers/withStandardTabInfo' ;
import { BROWSER _PANELS } from '../../../browser/static/js/constants' ;
import { usePgAdmin } from '../../../static/js/BrowserComponent' ;
import usePreferences from '../../../preferences/static/js/store' ;
import ErrorBoundary from '../../../static/js/helpers/ErrorBoundary' ;
2024-02-29 05:13:07 -06:00
import { parseApiError } from '../../../static/js/api_instance' ;
2024-03-28 01:49:34 -05:00
import SectionContainer from './components/SectionContainer' ;
import Replication from './Replication' ;
2024-05-07 06:01:04 -05:00
import { getExpandCell } from '../../../static/js/components/PgReactTableStyled' ;
2024-07-03 05:47:29 -05:00
import CodeMirror from '../../../static/js/components/ReactCodeMirror' ;
import GetAppRoundedIcon from '@mui/icons-material/GetAppRounded' ;
import { getBrowser } from '../../../static/js/utils' ;
import RefreshButton from './components/RefreshButtons' ;
2022-03-30 01:36:59 -05:00
function parseData ( data ) {
2022-09-08 04:46:48 -05:00
let res = [ ] ;
2022-03-30 01:36:59 -05:00
data . forEach ( ( row ) => {
res . push ( { ... row , icon : '' } ) ;
} ) ;
return res ;
}
2024-06-06 06:43:12 -05:00
const Root = styled ( 'div' ) ( ( { theme } ) => ( {
height : '100%' ,
width : '100%' ,
'& .Dashboard-dashboardPanel' : {
2022-03-30 01:36:59 -05:00
height : '100%' ,
background : theme . palette . grey [ 400 ] ,
2024-06-06 06:43:12 -05:00
'& .Dashboard-panelContent' : {
... theme . mixins . panelBorder . all ,
display : 'flex' ,
flexDirection : 'column' ,
overflow : 'hidden !important' ,
height : '100%' ,
width : '100%' ,
minHeight : '400px' ,
padding : '4px' ,
'& .Dashboard-mainTabs' : {
... theme . mixins . panelBorder . all ,
height : '100%' ,
display : 'flex' ,
flexDirection : 'column' ,
'& .Dashboard-terminateButton' : {
color : theme . palette . error . main
} ,
2024-07-03 05:47:29 -05:00
'& .Dashboard-download' : {
alignSelf : 'end' ,
'& .Dashboard-downloadButton' : {
width : '35px' ,
height : '30px'
} ,
} ,
'& .Dashboard-textArea' : {
height : '88%' ,
}
2024-06-06 06:43:12 -05:00
} ,
} ,
2022-03-30 01:36:59 -05:00
} ,
2024-06-06 06:43:12 -05:00
'& .Dashboard-emptyPanel' : {
2023-10-23 07:13:17 -05:00
width : '100%' ,
2024-06-19 01:24:18 -05:00
height : '100%' ,
2024-06-06 06:43:12 -05:00
background : theme . otherVars . emptySpaceBg ,
overflow : 'auto' ,
padding : '8px' ,
2023-02-09 22:58:39 -06:00
display : 'flex' ,
} ,
2022-03-30 01:36:59 -05:00
} ) ) ;
2024-05-07 06:01:04 -05:00
let activeQSchemaObj = new ActiveQuery ( ) ;
2024-07-03 05:47:29 -05:00
let serverLogSchemaObj = new ServerLog ( ) ;
2024-05-07 06:01:04 -05:00
2024-07-02 00:04:30 -05:00
const cellPropTypes = {
row : PropTypes . any ,
} ;
function getTerminateCell ( pgAdmin , sid , did , canTakeAction , onSuccess ) {
function TerminateCell ( { row } ) {
let terminate _session _url =
url _for ( 'dashboard.index' ) + 'terminate_session' + '/' + sid ,
title = gettext ( 'Terminate Session?' ) ,
txtConfirm = gettext (
'Are you sure you wish to terminate the session?'
) ,
txtSuccess = gettext ( 'Session terminated successfully.' ) ,
txtError = gettext (
'An error occurred whilst terminating the active query.'
) ;
const action _url = did
? terminate _session _url + '/' + did
: terminate _session _url ;
const api = getApiInstance ( ) ;
return (
< PgIconButton
size = "xs"
noBorder
icon = { < CancelIcon / > }
className = 'Dashboard-terminateButton'
onClick = { ( ) => {
if (
! canTakeAction ( row , 'terminate' )
)
return ;
let url = action _url + '/' + row . original . pid ;
pgAdmin . Browser . notifier . confirm (
title ,
txtConfirm ,
function ( ) {
api
. delete ( url )
. then ( function ( res ) {
if ( res . data == gettext ( 'Success' ) ) {
pgAdmin . Browser . notifier . success ( txtSuccess ) ;
onSuccess ? . ( ) ;
} else {
pgAdmin . Browser . notifier . error ( txtError ) ;
}
} )
. catch ( function ( error ) {
pgAdmin . Browser . notifier . alert (
gettext ( 'Failed to perform the operation.' ) ,
parseApiError ( error )
) ;
} ) ;
} ,
function ( ) {
return true ;
}
) ;
} }
aria - label = "Terminate Session?"
title = { gettext ( 'Terminate Session?' ) }
> < / PgIconButton >
) ;
}
TerminateCell . propTypes = cellPropTypes ;
return TerminateCell ;
}
function getCancelCell ( pgAdmin , sid , did , canTakeAction , onSuccess ) {
function CancelCell ( { row } ) {
let cancel _query _url =
url _for ( 'dashboard.index' ) + 'cancel_query' + '/' + sid ,
title = gettext ( 'Cancel Active Query?' ) ,
txtConfirm = gettext (
'Are you sure you wish to cancel the active query?'
) ,
txtSuccess = gettext ( 'Active query cancelled successfully.' ) ,
txtError = gettext (
'An error occurred whilst cancelling the active query.'
) ;
const action _url = did ? cancel _query _url + '/' + did : cancel _query _url ;
const api = getApiInstance ( ) ;
return (
< PgIconButton
size = "xs"
noBorder
icon = { < StopSharpIcon / > }
onClick = { ( ) => {
if ( ! canTakeAction ( row , 'cancel' ) )
return ;
let url = action _url + '/' + row . original . pid ;
pgAdmin . Browser . notifier . confirm (
title ,
txtConfirm ,
function ( ) {
api
. delete ( url )
. then ( function ( res ) {
if ( res . data == gettext ( 'Success' ) ) {
pgAdmin . Browser . notifier . success ( txtSuccess ) ;
onSuccess ? . ( ) ;
} else {
pgAdmin . Browser . notifier . error ( txtError ) ;
onSuccess ? . ( ) ;
}
} )
. catch ( function ( error ) {
pgAdmin . Browser . notifier . alert (
gettext ( 'Failed to perform the operation.' ) ,
parseApiError ( error )
) ;
} ) ;
} ,
function ( ) {
return true ;
}
) ;
} }
aria - label = "Cancel the query"
title = { gettext ( 'Cancel the active query' ) }
> < / PgIconButton >
) ;
}
CancelCell . propTypes = cellPropTypes ;
return CancelCell ;
}
2024-07-03 05:47:29 -05:00
function CustomRefresh ( { refresh , setRefresh } ) {
2024-07-02 00:04:30 -05:00
return (
2024-07-03 05:47:29 -05:00
< RefreshButton onClick = { ( e ) => {
e . preventDefault ( ) ;
setRefresh ( ! refresh ) ;
} } / >
) ;
}
CustomRefresh . propTypes = {
refresh : PropTypes . bool ,
setRefresh : PropTypes . func ,
} ;
function ActiveOnlyHeader ( { activeOnly , setActiveOnly , refresh , setRefresh } ) {
return ( < Fragment >
< RefreshButton onClick = { ( e ) => {
e . preventDefault ( ) ;
setRefresh ( ! refresh ) ;
} } / >
2024-07-02 00:04:30 -05:00
< InputCheckbox
label = { gettext ( 'Active sessions only' ) }
labelPlacement = "end"
className = 'Dashboard-searchInput'
onChange = { ( e ) => {
e . preventDefault ( ) ;
setActiveOnly ( e . target . checked ) ;
} }
value = { activeOnly }
controlProps = { {
label : gettext ( 'Active sessions only' ) ,
} }
2024-07-03 05:47:29 -05:00
/ > < / F r a g m e n t >
2024-07-02 00:04:30 -05:00
) ;
}
ActiveOnlyHeader . propTypes = {
activeOnly : PropTypes . bool ,
setActiveOnly : PropTypes . func ,
2024-07-03 05:47:29 -05:00
refresh : PropTypes . bool ,
setRefresh : PropTypes . func ,
2024-07-02 00:04:30 -05:00
} ;
2023-10-23 07:13:17 -05:00
function Dashboard ( {
nodeItem , nodeData , node , treeNodeInfo ,
2022-03-30 01:36:59 -05:00
... props
} ) {
2024-07-03 05:47:29 -05:00
const preferences = _ . merge (
usePreferences ( ) . getPreferencesForModule ( 'dashboards' ) ,
usePreferences ( ) . getPreferencesForModule ( 'graphs' ) ,
usePreferences ( ) . getPreferencesForModule ( 'misc' )
) ;
2024-06-06 06:43:12 -05:00
2024-07-03 05:47:29 -05:00
// Set Active tab depending on preferences setting
let activeTab = 0 ;
if ( ! _ . isUndefined ( preferences ) && ! preferences . show _graphs && preferences . show _activity ) activeTab = 1 ;
else if ( ! _ . isUndefined ( preferences ) && ! preferences . show _graphs && ! preferences . show _activity ) activeTab = 2 ;
const api = getApiInstance ( ) ;
2024-04-09 08:48:56 -05:00
const [ dashData , setDashData ] = useState ( [ ] ) ;
2022-03-30 01:36:59 -05:00
const [ msg , setMsg ] = useState ( '' ) ;
2023-09-27 05:34:48 -05:00
const [ ssMsg , setSsMsg ] = useState ( '' ) ;
2024-07-03 05:47:29 -05:00
const [ mainTabVal , setMainTabVal ] = useState ( activeTab ) ;
2022-04-11 07:12:16 -05:00
const [ refresh , setRefresh ] = useState ( false ) ;
2023-05-05 03:25:20 -05:00
const [ activeOnly , setActiveOnly ] = useState ( false ) ;
2023-09-27 05:34:48 -05:00
const [ systemStatsTabVal , setSystemStatsTabVal ] = useState ( 0 ) ;
2023-10-04 04:34:33 -05:00
const [ ldid , setLdid ] = useState ( 0 ) ;
2023-09-27 05:34:48 -05:00
2024-07-03 05:47:29 -05:00
const [ logCol , setLogCol ] = useState ( false ) ;
const [ logFormat , setLogFormat ] = useState ( 'T' ) ;
const [ logConfigFormat , setLogConfigFormat ] = useState ( [ ] ) ;
const [ nextPage , setNextPage ] = useState ( 0 ) ;
const [ hasNextPage , setHasNextPage ] = useState ( true ) ;
const [ isNextPageLoading , setIsNextPageLoading ] = useState ( false ) ;
2023-09-27 05:34:48 -05:00
const systemStatsTabChanged = ( e , tabVal ) => {
setSystemStatsTabVal ( tabVal ) ;
} ;
2023-10-23 07:13:17 -05:00
const pgAdmin = usePgAdmin ( ) ;
const did = treeNodeInfo ? . database ? . _id ? ? 0 ;
const sid = treeNodeInfo ? . server ? . _id ? ? 0 ;
const dbConnected = treeNodeInfo ? . database ? . connected ? ? false ;
const serverConnected = treeNodeInfo ? . server ? . connected ? ? false ;
const prefStore = usePreferences ( ) ;
2024-07-03 05:47:29 -05:00
let mainTabs = [ gettext ( 'Activity' ) , gettext ( 'State' ) ] ;
2022-03-30 01:36:59 -05:00
2024-07-03 05:47:29 -05:00
mainTabs . push ( gettext ( 'Configuration' ) , gettext ( 'Logs' ) , gettext ( 'System' ) ) ;
if ( treeNodeInfo ? . server ? . replication _type ) {
mainTabs . push ( gettext ( 'Replication' ) ) ;
2022-03-30 01:36:59 -05:00
}
2024-07-03 05:47:29 -05:00
let systemStatsTabs = [ gettext ( 'Summary' ) , gettext ( 'CPU' ) , gettext ( 'Memory' ) , gettext ( 'Storage' ) ] ;
2022-03-30 01:36:59 -05:00
2023-09-27 05:34:48 -05:00
const mainTabChanged = ( e , tabVal ) => {
setMainTabVal ( tabVal ) ;
} ;
2024-07-02 00:04:30 -05:00
const canTakeAction = ( row , cellAction ) => {
// We will validate if user is allowed to cancel the active query
// If there is only one active session means it probably our main
// connection session
cellAction = cellAction || null ;
let pg _version = treeNodeInfo . server . version || null ,
is _cancel _session = cellAction === 'cancel' ,
txtMessage ,
maintenance _database = treeNodeInfo . server . db ;
let maintenanceActiveSessions = dashData . filter ( ( data ) => data . state === 'active' &&
maintenance _database === data . datname ) ;
// With PG10, We have background process showing on dashboard
// We will not allow user to cancel them as they will fail with error
// anyway, so better usability we will throw our on notification
// Background processes do not have database field populated
if ( pg _version && pg _version >= 100000 && ! row . original . datname ) {
if ( is _cancel _session ) {
txtMessage = gettext ( 'You cannot cancel background worker processes.' ) ;
} else {
txtMessage = gettext (
'You cannot terminate background worker processes.'
) ;
}
pgAdmin . Browser . notifier . info ( txtMessage ) ;
return false ;
// If it is the last active connection on maintenance db then error out
} else if (
maintenance _database == row . original . datname &&
row . original . state == 'active' &&
maintenanceActiveSessions . length === 1
) {
if ( is _cancel _session ) {
txtMessage = gettext (
'You are not allowed to cancel the main active session.'
) ;
} else {
txtMessage = gettext (
'You are not allowed to terminate the main active session.'
) ;
}
pgAdmin . Browser . notifier . error ( txtMessage ) ;
return false ;
} else if ( is _cancel _session && row . original . state == 'idle' ) {
// If this session is already idle then do nothing
pgAdmin . Browser . notifier . info ( gettext ( 'The session is already in idle state.' ) ) ;
return false ;
} else {
// Will return true and let the backend handle all the cases.
// Added as fix of #7217
return true ;
}
} ;
2022-03-30 01:36:59 -05:00
const serverConfigColumns = [
{
2024-05-07 06:01:04 -05:00
accessorKey : 'name' ,
header : gettext ( 'Name' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 100 ,
size : 100 ,
} ,
{
accessorKey : 'category' ,
header : gettext ( 'Category' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 50 ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'setting' ,
header : gettext ( 'Value' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 100 ,
} ,
{
accessorKey : 'unit' ,
header : gettext ( 'Unit' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 30 ,
size : 30 ,
} ,
{
accessorKey : 'short_desc' ,
header : gettext ( 'Description' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
2022-03-30 01:36:59 -05:00
} ,
] ;
2024-07-03 05:47:29 -05:00
const downloadServerLogs = async ( ) => {
let extension = '.txt' ,
type = 'plain' ,
respData = '' ;
if ( logCol === false ) {
if ( logFormat == 'C' ) {
extension = '.csv' ;
type = 'csv' ;
} else if ( logFormat == 'J' ) {
extension = '.json' ;
type = 'json' ;
}
respData = dashData [ 0 ] [ 'pg_read_file' ] ;
} else if ( logCol === true ) {
extension = '.csv' ;
type = 'csv' ;
respData = dashData . map ( ( d ) => { return Object . values ( d ) . join ( ',' ) ; } ) . join ( '\n' ) ;
}
let fileName = 'data-' + new Date ( ) . getTime ( ) + extension ;
try {
let respBlob = new Blob ( [ respData ] , { type : 'text/' + type } ) ,
urlCreator = window . URL || window . webkitURL ,
download _url = urlCreator . createObjectURL ( respBlob ) ,
link = document . createElement ( 'a' ) ;
document . body . appendChild ( link ) ;
if ( getBrowser ( ) == 'IE' && window . navigator . msSaveBlob ) {
// IE10: (has Blob, but not a[download] or URL)
window . navigator . msSaveBlob ( respBlob , fileName ) ;
} else {
link . setAttribute ( 'href' , download _url ) ;
link . setAttribute ( 'download' , fileName ) ;
link . click ( ) ;
}
document . body . removeChild ( link ) ;
} catch ( error ) {
setSsMsg ( gettext ( 'Failed to download the logs.' ) ) ;
}
} ;
const serverLogColumns = [
{
header : ( ) => null ,
enableSorting : false ,
enableResizing : false ,
enableFilters : false ,
size : 35 ,
maxSize : 35 ,
minSize : 35 ,
id : 'btn-edit' ,
cell : getExpandCell ( {
title : gettext ( 'View the log details' )
} ) ,
} ,
{
accessorKey : 'error_severity' ,
header : gettext ( 'Error Severity' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
size : 100 ,
minSize : 35 ,
filterFn : 'equalsString'
} ,
{
accessorKey : 'timestamp' ,
header : gettext ( 'Log Prefix/Timestamp' ) ,
sortable : true ,
enableResizing : true ,
enableSorting : false ,
enableFilters : true ,
size : 150 ,
minSize : 35 ,
filterFn : 'equalsString'
} ,
{
accessorKey : 'message' ,
header : gettext ( 'Logs' ) ,
enableResizing : true ,
enableSorting : false ,
enableFilters : false ,
size : 35 ,
minSize : 200 ,
filterFn : 'equalsString'
} ,
] ;
2022-03-30 01:36:59 -05:00
const activityColumns = [
{
2024-05-07 06:01:04 -05:00
header : ( ) => null ,
enableSorting : true ,
enableResizing : false ,
enableFilters : false ,
size : 35 ,
maxSize : 35 ,
minSize : 35 ,
2022-03-30 01:36:59 -05:00
id : 'btn-terminate' ,
2024-07-02 00:04:30 -05:00
cell : getTerminateCell ( pgAdmin , sid , did , canTakeAction , setRefresh , ( ) => setRefresh ( ! refresh ) ) ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
header : ( ) => null ,
enableSorting : true ,
enableResizing : false ,
enableFilters : false ,
size : 35 ,
maxSize : 35 ,
minSize : 35 ,
2022-03-30 01:36:59 -05:00
id : 'btn-cancel' ,
2024-07-02 00:04:30 -05:00
cell : getCancelCell ( pgAdmin , sid , did , canTakeAction , setRefresh , ( ) => setRefresh ( ! refresh ) ) ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
header : ( ) => null ,
enableSorting : true ,
enableResizing : false ,
enableFilters : false ,
size : 35 ,
maxSize : 35 ,
minSize : 35 ,
2022-03-30 01:36:59 -05:00
id : 'btn-edit' ,
2024-05-07 06:01:04 -05:00
cell : getExpandCell ( {
2024-03-28 01:49:34 -05:00
title : gettext ( 'View the active session details' )
} ) ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'pid' ,
header : gettext ( 'PID' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 60 ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'datname' ,
header : gettext ( 'Database' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
enableVisibility : ! did ,
minSize : 50 ,
size : 80 ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'usename' ,
header : gettext ( 'User' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 60 ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'application_name' ,
header : gettext ( 'Application' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'client_addr' ,
header : gettext ( 'Client' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 100
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'backend_start' ,
header : gettext ( 'Backend start' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 100 ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'xact_start' ,
header : gettext ( 'Transaction start' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 100 ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'state' ,
header : gettext ( 'State' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 50 ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'waiting' ,
header : gettext ( 'Waiting' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
enableVisibility : treeNodeInfo ? . server ? . version < 90600
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'wait_event' ,
header : gettext ( 'Wait event' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'blocking_pids' ,
header : gettext ( 'Blocking PIDs' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
2022-03-30 01:36:59 -05:00
} ,
] ;
const databaseLocksColumns = [
{
2024-05-07 06:01:04 -05:00
accessorKey : 'pid' ,
header : gettext ( 'PID' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 50 ,
} ,
{
accessorKey : 'datname' ,
header : gettext ( 'Database' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
enableVisibility : ! did ,
minSize : 50 ,
size : 80 ,
} ,
{
accessorKey : 'locktype' ,
header : gettext ( 'Lock type' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 80 ,
} ,
{
accessorKey : 'relation' ,
header : gettext ( 'Target relation' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
} ,
{
accessorKey : 'page' ,
header : gettext ( 'Page' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 80 ,
} ,
{
accessorKey : 'tuple' ,
header : gettext ( 'Tuple' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 50 ,
} ,
{
accessorKey : 'virtualxid' ,
header : gettext ( 'vXID (target)' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 50 ,
} ,
{
accessorKey : 'transactionid' ,
header : gettext ( 'XID (target)' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 80 ,
} ,
{
accessorKey : 'classid' ,
header : gettext ( 'Class' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 80 ,
} ,
{
accessorKey : 'objid' ,
header : gettext ( 'Object ID' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 80 ,
} ,
{
accessorKey : 'virtualtransaction' ,
header : gettext ( 'vXID (owner)' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 50 ,
} ,
{
accessorKey : 'mode' ,
header : gettext ( 'Mode' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 50 ,
2022-03-30 01:36:59 -05:00
} ,
{
id : 'granted' ,
2024-05-07 06:01:04 -05:00
accessorKey : 'granted' ,
header : gettext ( 'Granted?' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
minSize : 50 ,
size : 80 ,
cell : ( { value } ) => String ( value )
2022-03-30 01:36:59 -05:00
} ,
] ;
const databasePreparedColumns = [
{
2024-05-07 06:01:04 -05:00
accessorKey : 'git' ,
header : gettext ( 'Name' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'datname' ,
header : gettext ( 'Database' ) ,
enableSorting : true ,
enableResizing : true ,
enableVisibility : ! did ,
enableFilters : true ,
2022-03-30 01:36:59 -05:00
minWidth : 26 ,
2022-05-18 07:19:54 -05:00
width : 80 ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'Owner' ,
header : gettext ( 'Owner' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'transaction' ,
header : gettext ( 'XID' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
2022-03-30 01:36:59 -05:00
} ,
{
2024-05-07 06:01:04 -05:00
accessorKey : 'prepared' ,
header : gettext ( 'Prepared at' ) ,
enableSorting : true ,
enableResizing : true ,
enableFilters : true ,
2022-03-30 01:36:59 -05:00
} ,
] ;
2022-04-14 00:41:45 -05:00
useEffect ( ( ) => {
2024-07-03 05:47:29 -05:00
if ( mainTabVal == 3 ) {
setLogFormat ( 'T' ) ;
let url = url _for ( 'dashboard.log_formats' ) + '/' + sid ;
api ( {
url : url ,
type : 'GET' ,
} )
. then ( ( res ) => {
let _format = res . data ;
let _frm = [
{ 'label' : gettext ( 'Text' ) , 'value' : 'T' , 'disabled' : ! _format . includes ( 'stderr' ) } ,
{ 'label' : gettext ( 'JSON' ) , 'value' : 'J' , 'disabled' : ! _format . includes ( 'jsonlog' ) } ,
{ 'label' : gettext ( 'CSV' ) , 'value' : 'C' , 'disabled' : ! _format . includes ( 'csvlog' ) }
] ;
setLogConfigFormat ( _frm ) ;
} )
. catch ( ( error ) => {
pgAdmin . Browser . notifier . alert (
gettext ( 'Failed to retrieve data from the server.' ) ,
_ . isUndefined ( error . response ) ? error . message : error . response . data . errormsg
) ;
} ) ;
}
} , [ nodeData , mainTabVal ] ) ;
2022-03-30 01:36:59 -05:00
useEffect ( ( ) => {
2024-07-03 05:47:29 -05:00
if ( mainTabVal == 0 ) return ;
2024-03-28 01:49:34 -05:00
// disable replication tab
2024-07-03 05:47:29 -05:00
if ( ! treeNodeInfo ? . server ? . replication _type && mainTabVal == 5 ) {
2024-03-28 01:49:34 -05:00
setMainTabVal ( 0 ) ;
}
2022-03-30 01:36:59 -05:00
let url ,
2023-09-27 05:34:48 -05:00
ssExtensionCheckUrl = url _for ( 'dashboard.check_system_statistics' ) ,
2022-03-30 01:36:59 -05:00
message = gettext (
'Please connect to the selected server to view the dashboard.'
) ;
2023-10-23 07:13:17 -05:00
if ( sid && serverConnected ) {
2022-03-30 01:36:59 -05:00
message = gettext ( 'Loading dashboard...' ) ;
2023-10-23 07:13:17 -05:00
if ( did && ! dbConnected ) return ;
2022-03-30 01:36:59 -05:00
2024-07-03 05:47:29 -05:00
if ( mainTabVal === 1 ) {
url = url _for ( 'dashboard.activity' ) ;
if ( did ) url += sid + '/' + did ;
else url += '/' + sid ;
} else if ( mainTabVal === 2 ) {
url = url _for ( 'dashboard.config' , { 'sid' : sid } ) ;
} else if ( mainTabVal === 3 ) {
if ( logCol === false ) {
url = url _for ( 'dashboard.logs' , { 'log_format' : logFormat , 'disp_format' : 'plain' , 'sid' : sid } ) ;
} else if ( logCol === true ) {
url = url _for ( 'dashboard.logs' , { 'log_format' : logFormat , 'disp_format' : 'table' , 'sid' : sid } ) ;
setNextPage ( 0 ) ;
}
}
2023-10-04 04:34:33 -05:00
if ( did && did > 0 ) ssExtensionCheckUrl += '/' + sid + '/' + did ;
2023-09-27 05:34:48 -05:00
else ssExtensionCheckUrl += '/' + sid ;
2022-03-30 01:36:59 -05:00
if ( node ) {
2024-07-03 05:47:29 -05:00
setSsMsg ( gettext ( 'Loading logs...' ) ) ;
setDashData ( [ ] ) ;
if ( mainTabVal != 4 && mainTabVal != 5 ) {
2023-10-04 04:34:33 -05:00
api ( {
url : url ,
type : 'GET' ,
2022-03-30 01:36:59 -05:00
} )
2023-10-04 04:34:33 -05:00
. then ( ( res ) => {
2024-07-03 05:47:29 -05:00
if ( res . data && res . data [ 'logs_disabled' ] ) {
setSsMsg ( gettext ( 'Please enable the logging to view the server logs.' ) ) ;
} else {
setDashData ( parseData ( res . data ) ) ;
}
2023-10-04 04:34:33 -05:00
} )
. catch ( ( error ) => {
2023-10-23 07:13:17 -05:00
pgAdmin . Browser . notifier . alert (
2023-10-04 04:34:33 -05:00
gettext ( 'Failed to retrieve data from the server.' ) ,
_ . isUndefined ( error . response ) ? error . message : error . response . data . errormsg
) ;
// show failed message.
setMsg ( gettext ( 'Failed to retrieve data from the server.' ) ) ;
} ) ;
}
2024-07-03 05:47:29 -05:00
else if ( mainTabVal == 4 ) {
2023-10-04 04:34:33 -05:00
api ( {
url : ssExtensionCheckUrl ,
type : 'GET' ,
2023-09-27 05:34:48 -05:00
} )
2023-10-04 04:34:33 -05:00
. then ( ( res ) => {
const data = res . data ;
2024-04-09 08:48:56 -05:00
if ( ! data [ 'ss_present' ] ) {
2024-03-12 00:20:09 -05:00
setSsMsg ( gettext ( 'The system_stats extension is not installed. You can install the extension in a database using the "CREATE EXTENSION system_stats;" SQL command. Reload pgAdmin once it is installed.' ) ) ;
2023-10-04 04:34:33 -05:00
setLdid ( 0 ) ;
} else {
setSsMsg ( 'installed' ) ;
setLdid ( did ) ;
}
} )
. catch ( ( ) => {
setSsMsg ( gettext ( 'Failed to verify the presence of system stats extension.' ) ) ;
setLdid ( 0 ) ;
} ) ;
} else {
setSsMsg ( '' ) ;
setLdid ( 0 ) ;
}
2022-03-30 01:36:59 -05:00
} else {
setMsg ( message ) ;
}
}
if ( message != '' ) {
setMsg ( message ) ;
}
2024-07-03 05:47:29 -05:00
} , [ nodeData , treeNodeInfo , prefStore , refresh , mainTabVal , logCol , logFormat ] ) ;
2022-04-04 07:12:42 -05:00
2023-05-05 03:25:20 -05:00
const filteredDashData = useMemo ( ( ) => {
2024-07-03 05:47:29 -05:00
if ( mainTabVal == 1 && activeOnly ) {
2023-05-05 03:25:20 -05:00
// we want to show 'idle in transaction', 'active', 'active in transaction', and future non-blank, non-"idle" status values
2024-07-03 05:47:29 -05:00
return dashData [ 0 ] [ 'activity' ] . filter ( ( r ) => ( r . state && r . state != '' && r . state != 'idle' ) ) ;
2023-05-05 03:25:20 -05:00
}
2024-07-03 05:47:29 -05:00
return dashData && dashData [ 0 ] && dashData [ 0 ] [ 'activity' ] || [ ] ;
} , [ dashData , activeOnly , mainTabVal ] ) ;
2023-05-05 03:25:20 -05:00
2022-09-10 03:30:22 -05:00
const showDefaultContents = ( ) => {
return (
2023-10-23 07:13:17 -05:00
sid && ! serverConnected ? (
2024-06-06 06:43:12 -05:00
< Box className = 'Dashboard-dashboardPanel' >
< div className = 'Dashboard-emptyPanel' >
2022-09-10 03:30:22 -05:00
< EmptyPanelMessage text = { msg } / >
< / div >
< / Box >
) : (
< WelcomeDashboard
2023-10-23 07:13:17 -05:00
pgBrowser = { pgAdmin . Browser }
2022-09-10 03:30:22 -05:00
node = { node }
itemData = { nodeData }
2023-10-23 07:13:17 -05:00
item = { nodeItem }
2022-09-10 03:30:22 -05:00
sid = { sid }
did = { did }
/ >
)
) ;
} ;
2024-07-03 05:47:29 -05:00
const CustomLogHeaderLabel =
{
label : gettext ( 'Table based logs' ) ,
} ;
const CustomLogHeader = ( ) => {
return ( < Box className = 'Dashboard-cardHeader' display = "flex" flexDirection = "column" >
< FormInputToggle
label = { gettext ( 'Log Format' ) }
className = 'Dashboard-searchInput'
value = { logFormat }
onChange = { ( val ) => {
setLogFormat ( val ) ;
} }
options = { logConfigFormat }
controlProps = { CustomLogHeaderLabel }
labelGridBasis = { 3 }
controlGridBasis = { 6 }
> < / FormInputToggle >
< FormInputSwitch
label = { gettext ( 'Logs in tabular format ?' ) }
labelPlacement = "end"
className = 'Dashboard-searchInput'
value = { logCol }
onChange = { ( e ) => {
setDashData ( [ ] ) ;
setLogCol ( e . target . checked ) ;
} }
controlProps = { CustomLogHeaderLabel }
labelGridBasis = { 3 }
controlGridBasis = { 6 }
> < / FormInputSwitch >
< div className = 'Dashboard-download' > < PgIconButton
size = "xs"
className = 'Dashboard-downloadButton'
icon = { < GetAppRoundedIcon / > }
onClick = { downloadServerLogs }
aria - label = "Download"
title = { gettext ( 'Download logs ' ) }
> < / PgIconButton > < / div >
< / Box > ) ;
} ;
const loadNextPage = ( ) => {
setIsNextPageLoading ( true ) ;
setTimeout ( ( ) => {
setHasNextPage ( true ) ;
setIsNextPageLoading ( false ) ;
let _url = url _for ( 'dashboard.logs' , { 'log_format' : logFormat , 'disp_format' : 'table' , 'sid' : sid } ) ;
_url += '/' + ( nextPage + 1 ) ;
const api = getApiInstance ( ) ;
api ( {
url : _url ,
type : 'GET' ,
} )
. then ( ( res ) => {
console . warn ( res . data . length ) ;
if ( res . data && res . data . length > 0 ) {
let _d = dashData . concat ( parseData ( res . data ) ) ;
setDashData ( _d ) ;
setNextPage ( nextPage + 1 ) ;
}
} )
. catch ( ( error ) => {
pgAdmin . Browser . notifier . alert (
gettext ( 'Failed to retrieve data from the server.' ) ,
_ . isUndefined ( error . response ) ? error . message : error . response . data . errormsg
) ;
// show failed message.
setMsg ( gettext ( 'Failed to retrieve data from the server.' ) ) ;
} ) ;
} , 500 ) ;
} ;
2022-03-30 01:36:59 -05:00
return (
2024-06-06 06:43:12 -05:00
( < Root >
2023-10-23 07:13:17 -05:00
{ sid && serverConnected ? (
2024-06-06 06:43:12 -05:00
< Box className = 'Dashboard-dashboardPanel' >
< Box className = 'Dashboard-panelContent' >
< Box className = 'Dashboard-mainTabs' >
2023-10-23 07:13:17 -05:00
< Box >
< Tabs
value = { mainTabVal }
onChange = { mainTabChanged }
>
2024-07-03 05:47:29 -05:00
{ mainTabs . map ( ( tabValue , i ) => {
if ( tabValue == 'Activity' ) {
if ( ! _ . isUndefined ( preferences ) && preferences . show _graphs ) {
return < Tab key = { tabValue } label = { tabValue } value = { i } / > ;
}
} else if ( tabValue == 'State' ) {
if ( ! _ . isUndefined ( preferences ) && preferences . show _activity ) {
return < Tab key = { tabValue } label = { tabValue } value = { i } / > ;
}
}
else {
return < Tab key = { tabValue } label = { tabValue } value = { i } / > ;
}
2023-10-23 07:13:17 -05:00
} ) }
< / Tabs >
< / Box >
2024-07-03 05:47:29 -05:00
{ /* Server Activity */ }
{ ! _ . isUndefined ( preferences ) && preferences . show _graphs && (
< TabPanel value = { mainTabVal } index = { 0 } >
2023-10-23 07:13:17 -05:00
< Graphs
key = { sid + did }
preferences = { preferences }
sid = { sid }
did = { did }
pageVisible = { props . isActive }
> < / Graphs >
2024-07-03 05:47:29 -05:00
< / TabPanel >
) }
{ /* Server Activity */ }
< TabPanel value = { mainTabVal } index = { 1 } classNameRoot = 'Dashboard-tabPanel' >
2023-10-23 07:13:17 -05:00
{ ! _ . isUndefined ( preferences ) && preferences . show _activity && (
2024-07-03 05:47:29 -05:00
< Fragment >
< SectionContainer title = { gettext ( 'Sessions' ) } style = { { height : 'auto' , minHeight : '200px' , paddingBottom : '20px' } }
>
2024-03-28 01:49:34 -05:00
< PgTable
caveTable = { false }
2024-05-07 06:01:04 -05:00
tableNoBorder = { false }
2024-07-03 05:47:29 -05:00
customHeader = { < ActiveOnlyHeader activeOnly = { activeOnly } setActiveOnly = { setActiveOnly } refresh = { refresh } setRefresh = { setRefresh } / > }
2024-03-28 01:49:34 -05:00
columns = { activityColumns }
2024-07-03 05:47:29 -05:00
data = { ( dashData !== undefined && dashData [ 0 ] && filteredDashData ) || [ ] }
2024-05-07 06:01:04 -05:00
schema = { activeQSchemaObj }
2024-03-28 01:49:34 -05:00
> < / PgTable >
2024-07-03 05:47:29 -05:00
< / SectionContainer >
< SectionContainer title = { gettext ( 'Locks' ) } style = { { height : 'auto' , minHeight : '200px' , paddingBottom : '20px' } } >
2024-03-28 01:49:34 -05:00
< PgTable
2024-07-03 05:47:29 -05:00
customHeader = { < CustomRefresh refresh = { refresh } setRefresh = { setRefresh } / > }
2024-03-28 01:49:34 -05:00
caveTable = { false }
2024-05-07 06:01:04 -05:00
tableNoBorder = { false }
2024-03-28 01:49:34 -05:00
columns = { databaseLocksColumns }
2024-07-03 05:47:29 -05:00
data = { ( dashData !== undefined && dashData [ 0 ] && dashData [ 0 ] [ 'locks' ] ) || [ ] }
2024-03-28 01:49:34 -05:00
> < / PgTable >
2024-07-03 05:47:29 -05:00
< / SectionContainer >
< SectionContainer title = { gettext ( 'Prepared Transactions' ) } style = { { height : 'auto' , minHeight : '200px' , paddingBottom : '20px' } } >
2024-03-28 01:49:34 -05:00
< PgTable
2024-07-03 05:47:29 -05:00
customHeader = { < CustomRefresh refresh = { refresh } setRefresh = { setRefresh } / > }
2024-03-28 01:49:34 -05:00
caveTable = { false }
2024-05-07 06:01:04 -05:00
tableNoBorder = { false }
2024-03-28 01:49:34 -05:00
columns = { databasePreparedColumns }
2024-07-03 05:47:29 -05:00
data = { ( dashData !== undefined && dashData [ 0 ] && dashData [ 0 ] [ 'prepared' ] ) || [ ] }
2024-03-28 01:49:34 -05:00
> < / PgTable >
2024-07-03 05:47:29 -05:00
< / SectionContainer >
< / Fragment >
2023-10-23 07:13:17 -05:00
) }
< / TabPanel >
2024-07-03 05:47:29 -05:00
{ /* Server Configuration */ }
< TabPanel value = { mainTabVal } index = { 2 } classNameRoot = 'Dashboard-tabPanel' >
< PgTable
caveTable = { false }
tableNoBorder = { false }
columns = { serverConfigColumns }
data = { dashData }
> < / PgTable >
< / TabPanel >
{ /* Server Logs */ }
< TabPanel value = { mainTabVal } index = { 3 } classNameRoot = 'Dashboard-tabPanel' >
{ dashData && dashData . length != 0 &&
< CustomLogHeader / > }
{ dashData . length == 0 && < div className = 'Dashboard-emptyPanel' >
< EmptyPanelMessage text = { ssMsg } / >
< / div > }
{ dashData && logCol === false && dashData . length == 1 && < CodeMirror
id = 'tests'
language = { logFormat == 'J' ? 'json' : 'pgsql' }
className = 'Dashboard-textArea'
value = { dashData [ 0 ] [ 'pg_read_file' ] }
readonly = { true }
options = { {
lineNumbers : true ,
mode : 'text/plain' ,
} }
/ > }
{ dashData && logCol === true && < PgTable
caveTable = { false }
tableNoBorder = { false }
columns = { serverLogColumns }
data = { dashData }
hasNextPage = { hasNextPage }
isNextPageLoading = { isNextPageLoading }
loadNextPage = { loadNextPage }
schema = { serverLogSchemaObj }
> < / PgTable > }
< / TabPanel >
2023-10-23 07:13:17 -05:00
{ /* System Statistics */ }
2024-07-03 05:47:29 -05:00
< TabPanel value = { mainTabVal } index = { 4 } classNameRoot = 'Dashboard-tabPanel' >
2023-10-23 07:13:17 -05:00
< Box height = "100%" display = "flex" flexDirection = "column" >
{ ssMsg === 'installed' && did === ldid ?
< ErrorBoundary >
< Box >
< Tabs
value = { systemStatsTabVal }
onChange = { systemStatsTabChanged }
>
{ systemStatsTabs . map ( ( tabValue ) => {
return < Tab key = { tabValue } label = { tabValue } / > ;
} ) }
< / Tabs >
< / Box >
2024-06-06 06:43:12 -05:00
< TabPanel value = { systemStatsTabVal } index = { 0 } classNameRoot = 'Dashboard-tabPanel' >
2023-10-23 07:13:17 -05:00
< Summary
key = { sid + did }
preferences = { preferences }
sid = { sid }
did = { did }
pageVisible = { props . isActive }
serverConnected = { serverConnected }
/ >
< / TabPanel >
2024-06-06 06:43:12 -05:00
< TabPanel value = { systemStatsTabVal } index = { 1 } classNameRoot = 'Dashboard-tabPanel' >
2024-07-02 00:04:30 -05:00
< CpuDetails
2023-10-23 07:13:17 -05:00
key = { sid + did }
preferences = { preferences }
sid = { sid }
did = { did }
pageVisible = { props . isActive }
serverConnected = { serverConnected }
/ >
< / TabPanel >
2024-06-06 06:43:12 -05:00
< TabPanel value = { systemStatsTabVal } index = { 2 } classNameRoot = 'Dashboard-tabPanel' >
2023-10-23 07:13:17 -05:00
< Memory
key = { sid + did }
preferences = { preferences }
sid = { sid }
did = { did }
pageVisible = { props . isActive }
serverConnected = { serverConnected }
/ >
< / TabPanel >
2024-06-06 06:43:12 -05:00
< TabPanel value = { systemStatsTabVal } index = { 3 } classNameRoot = 'Dashboard-tabPanel' >
2023-10-23 07:13:17 -05:00
< Storage
key = { sid + did }
preferences = { preferences }
sid = { sid }
did = { did }
pageVisible = { props . isActive }
serverConnected = { serverConnected }
systemStatsTabVal = { systemStatsTabVal }
/ >
< / TabPanel >
< / ErrorBoundary > :
2024-06-06 06:43:12 -05:00
< div className = 'Dashboard-emptyPanel' >
2023-10-23 07:13:17 -05:00
< EmptyPanelMessage text = { ssMsg } / >
< / div >
}
< / Box >
< / TabPanel >
2024-03-28 01:49:34 -05:00
{ /* Replication */ }
2024-07-03 05:47:29 -05:00
< TabPanel value = { mainTabVal } index = { 5 } classNameRoot = 'Dashboard-tabPanel' >
2024-05-20 06:24:49 -05:00
< Replication key = { sid } sid = { sid } node = { node }
preferences = { preferences } treeNodeInfo = { treeNodeInfo } nodeData = { nodeData } pageVisible = { props . isActive } / >
2024-03-28 01:49:34 -05:00
< / TabPanel >
2023-09-27 05:34:48 -05:00
< / Box >
2022-03-30 01:36:59 -05:00
< / Box >
< / Box >
2022-09-10 03:30:22 -05:00
) : showDefaultContents ( ) }
2024-06-06 06:43:12 -05:00
< / Root > )
2022-03-30 01:36:59 -05:00
) ;
}
Dashboard . propTypes = {
node : PropTypes . func ,
itemData : PropTypes . object ,
nodeData : PropTypes . object ,
treeNodeInfo : PropTypes . object ,
2023-10-23 07:13:17 -05:00
nodeItem : PropTypes . object ,
2022-03-30 01:36:59 -05:00
preferences : PropTypes . object ,
sid : PropTypes . string ,
did : PropTypes . oneOfType ( [ PropTypes . bool , PropTypes . number ] ) ,
row : PropTypes . object ,
serverConnected : PropTypes . bool ,
2022-04-05 03:10:22 -05:00
dbConnected : PropTypes . bool ,
2023-10-23 07:13:17 -05:00
isActive : PropTypes . bool ,
2024-07-03 05:47:29 -05:00
column : PropTypes . object ,
2022-03-30 01:36:59 -05:00
} ;
2023-10-23 07:13:17 -05:00
export default withStandardTabInfo ( Dashboard , BROWSER _PANELS . DASHBOARD ) ;