Fix healthcheck and tradingbot positions
This commit is contained in:
@@ -411,6 +411,8 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
{
|
{
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
$"Position {signal.Identifier} don't need to be update. Position still opened");
|
$"Position {signal.Identifier} don't need to be update. Position still opened");
|
||||||
|
|
||||||
|
await SetPositionStatus(signal.Identifier, PositionStatus.Filled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,6 +447,10 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
{
|
{
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
$"Position {signal.Identifier} don't need to be update. Position still opened");
|
$"Position {signal.Identifier} don't need to be update. Position still opened");
|
||||||
|
|
||||||
|
position.Status = PositionStatus.Filled;
|
||||||
|
|
||||||
|
await SetPositionStatus(signal.Identifier, PositionStatus.Filled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
import { ArrowDownIcon, ArrowUpIcon } from '@heroicons/react/solid'
|
import {ArrowDownIcon, ArrowUpIcon} from '@heroicons/react/solid'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
import {useExpanded, useFilters, usePagination, useSortBy, useTable,} from 'react-table'
|
||||||
useTable,
|
|
||||||
usePagination,
|
|
||||||
useSortBy,
|
|
||||||
useFilters,
|
|
||||||
useExpanded,
|
|
||||||
} from 'react-table'
|
|
||||||
|
|
||||||
import type { TableInstanceWithHooks } from '../../../global/type'
|
import type {TableInstanceWithHooks} from '../../../global/type'
|
||||||
|
|
||||||
// Define a default UI for filtering
|
// Define a default UI for filtering
|
||||||
function DefaultColumnFilter({
|
function DefaultColumnFilter({
|
||||||
@@ -31,7 +25,7 @@ export default function Table({
|
|||||||
columns,
|
columns,
|
||||||
data,
|
data,
|
||||||
renderRowSubCompontent,
|
renderRowSubCompontent,
|
||||||
showPagination,
|
showPagination = true,
|
||||||
hiddenColumns,
|
hiddenColumns,
|
||||||
showTotal = false,
|
showTotal = false,
|
||||||
}: any) {
|
}: any) {
|
||||||
@@ -49,10 +43,7 @@ export default function Table({
|
|||||||
headerGroups,
|
headerGroups,
|
||||||
prepareRow,
|
prepareRow,
|
||||||
visibleColumns,
|
visibleColumns,
|
||||||
page, // Instead of using 'rows', we'll use page,
|
page,
|
||||||
// which has only the rows for the active page
|
|
||||||
|
|
||||||
// The rest of these things are super handy, too ;)
|
|
||||||
canPreviousPage,
|
canPreviousPage,
|
||||||
canNextPage,
|
canNextPage,
|
||||||
pageOptions,
|
pageOptions,
|
||||||
|
|||||||
@@ -1,25 +1,14 @@
|
|||||||
import {
|
import {ArrowDownIcon, ArrowUpIcon, ChevronDownIcon, ChevronRightIcon, PlayIcon,} from '@heroicons/react/solid'
|
||||||
ArrowDownIcon,
|
import React, {useEffect, useState} from 'react'
|
||||||
ArrowUpIcon,
|
|
||||||
ChevronDownIcon,
|
|
||||||
ChevronRightIcon,
|
|
||||||
PlayIcon,
|
|
||||||
} from '@heroicons/react/solid'
|
|
||||||
import React, { useEffect, useState } from 'react'
|
|
||||||
|
|
||||||
import { Hub } from '../../../app/providers/Hubs'
|
import {Hub} from '../../../app/providers/Hubs'
|
||||||
import useApiUrlStore from '../../../app/store/apiStore'
|
import useApiUrlStore from '../../../app/store/apiStore'
|
||||||
import type { Account, TradingBot } from '../../../generated/ManagingApi'
|
import type {Account, TradingBot} from '../../../generated/ManagingApi'
|
||||||
import {
|
import {AccountClient, BotClient, TradeDirection, TradeStatus,} from '../../../generated/ManagingApi'
|
||||||
AccountClient,
|
import {SelectColumnFilter, Table} from '../../mollecules'
|
||||||
BotClient,
|
|
||||||
TradeDirection,
|
|
||||||
TradeStatus,
|
|
||||||
} from '../../../generated/ManagingApi'
|
|
||||||
import { SelectColumnFilter, Table } from '../../mollecules'
|
|
||||||
import BacktestRowDetails from '../Backtest/backtestRowDetails'
|
|
||||||
import StatusBadge from '../StatusBadge/StatusBadge'
|
import StatusBadge from '../StatusBadge/StatusBadge'
|
||||||
import Summary from '../Trading/Summary'
|
import Summary from '../Trading/Summary'
|
||||||
|
import BotRowDetails from './botRowDetails'
|
||||||
|
|
||||||
export default function ActiveBots() {
|
export default function ActiveBots() {
|
||||||
const [bots, setBots] = useState<TradingBot[]>([])
|
const [bots, setBots] = useState<TradingBot[]>([])
|
||||||
@@ -63,10 +52,14 @@ export default function ActiveBots() {
|
|||||||
Header: 'Status',
|
Header: 'Status',
|
||||||
accessor: 'status',
|
accessor: 'status',
|
||||||
disableFilters: true,
|
disableFilters: true,
|
||||||
sortType: 'basic',
|
disableSortBy: true,
|
||||||
|
search: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: 'isForWatchingOnly',
|
accessor: 'isForWatchingOnly',
|
||||||
|
disableFilters: true,
|
||||||
|
disableSortBy: true,
|
||||||
|
search: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Filter: SelectColumnFilter,
|
Filter: SelectColumnFilter,
|
||||||
@@ -185,10 +178,9 @@ export default function ActiveBots() {
|
|||||||
const renderRowSubComponent = React.useCallback(
|
const renderRowSubComponent = React.useCallback(
|
||||||
({ row }: any) => (
|
({ row }: any) => (
|
||||||
<>
|
<>
|
||||||
<BacktestRowDetails
|
<BotRowDetails
|
||||||
candles={row.original.candles}
|
bot={row.original}
|
||||||
positions={row.original.positions}
|
></BotRowDetails>
|
||||||
></BacktestRowDetails>
|
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[]
|
[]
|
||||||
@@ -204,6 +196,7 @@ export default function ActiveBots() {
|
|||||||
columns={columns}
|
columns={columns}
|
||||||
data={bots}
|
data={bots}
|
||||||
renderRowSubCompontent={renderRowSubComponent}
|
renderRowSubCompontent={renderRowSubComponent}
|
||||||
|
hiddenColumns={['isForWatchingOnly']}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,315 +1,300 @@
|
|||||||
import React, {useEffect, useState} from 'react'
|
import React from 'react'
|
||||||
|
import {useQuery} from '@tanstack/react-query'
|
||||||
import useApiUrlStore from '../../../app/store/apiStore'
|
import useApiUrlStore from '../../../app/store/apiStore'
|
||||||
import {Table} from '../../../components/mollecules'
|
import {Table} from '../../../components/mollecules'
|
||||||
|
|
||||||
// Define health check response interface based on the provided example
|
// Define health check response interface based on the provided example
|
||||||
interface HealthCheckEntry {
|
interface HealthCheckEntry {
|
||||||
data: Record<string, any>
|
data: Record<string, any>
|
||||||
duration: string
|
duration: string
|
||||||
status: string
|
status: string
|
||||||
tags: string[]
|
tags: string[]
|
||||||
description?: string
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HealthCheckResponse {
|
interface HealthCheckResponse {
|
||||||
status: string
|
status: string
|
||||||
totalDuration: string
|
totalDuration: string
|
||||||
entries: Record<string, HealthCheckEntry>
|
entries: Record<string, HealthCheckEntry>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interface for candle timeframe check data
|
// Interface for candle timeframe check data
|
||||||
interface CandleTimeframeCheck {
|
interface CandleTimeframeCheck {
|
||||||
CheckedTicker: string
|
CheckedTicker: string
|
||||||
CheckedTimeframe: string
|
CheckedTimeframe: string
|
||||||
StartDate: string
|
StartDate: string
|
||||||
LatestCandleDate?: string
|
LatestCandleDate?: string
|
||||||
TimeDifference?: string
|
TimeDifference?: string
|
||||||
Status: string
|
Status: string
|
||||||
Message: string
|
Message: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Web3ProxyHealthDetail {
|
interface Web3ProxyHealthDetail {
|
||||||
status: string
|
status: string
|
||||||
message: string
|
message: string
|
||||||
data?: Record<string, any>
|
data?: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
const HealthChecks: React.FC = () => {
|
const HealthChecks: React.FC = () => {
|
||||||
const { apiUrl, workerUrl } = useApiUrlStore()
|
const {apiUrl, workerUrl} = useApiUrlStore()
|
||||||
const [apiHealth, setApiHealth] = useState<HealthCheckResponse | null>(null)
|
|
||||||
const [workerHealth, setWorkerHealth] = useState<HealthCheckResponse | null>(null)
|
|
||||||
const [web3ProxyHealth, setWeb3ProxyHealth] = useState<HealthCheckResponse | null>(null)
|
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
|
||||||
|
|
||||||
useEffect(() => {
|
// Use TanStack Query for API health check
|
||||||
const fetchHealthChecks = async () => {
|
const {data: apiHealth, isLoading: isLoadingApi} = useQuery({
|
||||||
setIsLoading(true)
|
queryKey: ['health', 'api'],
|
||||||
try {
|
queryFn: async () => {
|
||||||
// Fetch API health check
|
const response = await fetch(`${apiUrl}/health`)
|
||||||
const apiResponse = await fetch(`${apiUrl}/health`)
|
if (!response.ok) throw new Error('Failed to fetch API health')
|
||||||
if (apiResponse.ok) {
|
return response.json() as Promise<HealthCheckResponse>
|
||||||
const data = await apiResponse.json()
|
}
|
||||||
setApiHealth(data)
|
})
|
||||||
|
|
||||||
|
// Use TanStack Query for Worker health check
|
||||||
|
const {data: workerHealth, isLoading: isLoadingWorker} = useQuery({
|
||||||
|
queryKey: ['health', 'worker'],
|
||||||
|
queryFn: async () => {
|
||||||
|
const response = await fetch(`${workerUrl}/health`)
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch Worker health')
|
||||||
|
return response.json() as Promise<HealthCheckResponse>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// Determine overall loading state
|
||||||
|
const isLoading = isLoadingApi || isLoadingWorker
|
||||||
|
|
||||||
|
// Helper function to prepare table data from health response
|
||||||
|
const prepareHealthData = (
|
||||||
|
service: string,
|
||||||
|
health: HealthCheckResponse | null
|
||||||
|
) => {
|
||||||
|
if (!health) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
service,
|
||||||
|
component: 'N/A',
|
||||||
|
status: 'Unreachable',
|
||||||
|
duration: 'N/A',
|
||||||
|
tags: 'N/A',
|
||||||
|
description: 'Service unreachable',
|
||||||
|
details: null,
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch Worker health check
|
// Convert entries to rows for the table
|
||||||
const workerResponse = await fetch(`${workerUrl}/health`)
|
const results: any[] = [];
|
||||||
if (workerResponse.ok) {
|
|
||||||
const data = await workerResponse.json()
|
|
||||||
setWorkerHealth(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch Web3Proxy health check - use the dedicated endpoint we created
|
Object.entries(health.entries).forEach(([key, entry]) => {
|
||||||
const web3Response = await fetch(`${apiUrl}/health/web3proxy`)
|
// Basic health check entry
|
||||||
if (web3Response.ok) {
|
const baseEntry = {
|
||||||
const data = await web3Response.json()
|
service,
|
||||||
setWeb3ProxyHealth(data)
|
component: key,
|
||||||
}
|
status: entry.status,
|
||||||
} catch (error) {
|
duration: entry.duration,
|
||||||
console.error('Error fetching health checks:', error)
|
tags: entry.tags.join(', '),
|
||||||
} finally {
|
description: entry.description || '',
|
||||||
setIsLoading(false)
|
details: null,
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
fetchHealthChecks()
|
// Add the base entry
|
||||||
}, [apiUrl, workerUrl])
|
results.push(baseEntry);
|
||||||
|
|
||||||
// Helper function to prepare table data from health response
|
// Special handling for candle-data to expand timeframe checks
|
||||||
const prepareHealthData = (
|
if (key === 'candle-data' && entry.data) {
|
||||||
service: string,
|
// Extract timeframe checks
|
||||||
health: HealthCheckResponse | null
|
Object.entries(entry.data)
|
||||||
) => {
|
.filter(([dataKey]) => dataKey.startsWith('TimeframeCheck_'))
|
||||||
if (!health) {
|
.forEach(([dataKey, timeframeData]) => {
|
||||||
return [
|
const tfData = timeframeData as CandleTimeframeCheck;
|
||||||
{
|
results.push({
|
||||||
service,
|
service,
|
||||||
component: 'N/A',
|
component: `${key} - ${tfData.CheckedTimeframe}`,
|
||||||
status: 'Unreachable',
|
status: tfData.Status,
|
||||||
duration: 'N/A',
|
duration: '',
|
||||||
tags: 'N/A',
|
tags: 'candles',
|
||||||
description: 'Service unreachable',
|
description: tfData.Message,
|
||||||
details: null,
|
details: {
|
||||||
},
|
Ticker: tfData.CheckedTicker,
|
||||||
]
|
LatestCandle: tfData.LatestCandleDate,
|
||||||
}
|
TimeDifference: tfData.TimeDifference,
|
||||||
|
},
|
||||||
// Convert entries to rows for the table
|
});
|
||||||
const results: any[] = [];
|
});
|
||||||
|
|
||||||
Object.entries(health.entries).forEach(([key, entry]) => {
|
|
||||||
// Basic health check entry
|
|
||||||
const baseEntry = {
|
|
||||||
service,
|
|
||||||
component: key,
|
|
||||||
status: entry.status,
|
|
||||||
duration: entry.duration,
|
|
||||||
tags: entry.tags.join(', '),
|
|
||||||
description: entry.description || '',
|
|
||||||
details: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add the base entry
|
|
||||||
results.push(baseEntry);
|
|
||||||
|
|
||||||
// Special handling for candle-data to expand timeframe checks
|
|
||||||
if (key === 'candle-data' && entry.data) {
|
|
||||||
// Extract timeframe checks
|
|
||||||
Object.entries(entry.data)
|
|
||||||
.filter(([dataKey]) => dataKey.startsWith('TimeframeCheck_'))
|
|
||||||
.forEach(([dataKey, timeframeData]) => {
|
|
||||||
const tfData = timeframeData as CandleTimeframeCheck;
|
|
||||||
results.push({
|
|
||||||
service,
|
|
||||||
component: `${key} - ${tfData.CheckedTimeframe}`,
|
|
||||||
status: tfData.Status,
|
|
||||||
duration: '',
|
|
||||||
tags: 'candles',
|
|
||||||
description: tfData.Message,
|
|
||||||
details: {
|
|
||||||
Ticker: tfData.CheckedTicker,
|
|
||||||
LatestCandle: tfData.LatestCandleDate,
|
|
||||||
TimeDifference: tfData.TimeDifference,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special handling for Web3Proxy components
|
|
||||||
if (key === 'web3proxy' && entry.data) {
|
|
||||||
// Handle Privy check if present
|
|
||||||
if (entry.data.privy) {
|
|
||||||
const privyData = entry.data.privy as Web3ProxyHealthDetail;
|
|
||||||
results.push({
|
|
||||||
service,
|
|
||||||
component: `${key} - Privy`,
|
|
||||||
status: privyData.status,
|
|
||||||
duration: '',
|
|
||||||
tags: 'privy, external',
|
|
||||||
description: privyData.message || '',
|
|
||||||
details: null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle GMX check if present
|
|
||||||
if (entry.data.gmx) {
|
|
||||||
const gmxData = entry.data.gmx as Web3ProxyHealthDetail;
|
|
||||||
const marketDetails: Record<string, any> = {};
|
|
||||||
|
|
||||||
// Add market count and response time if available
|
|
||||||
if (gmxData.data) {
|
|
||||||
if (gmxData.data.marketCount) {
|
|
||||||
marketDetails['Market Count'] = gmxData.data.marketCount;
|
|
||||||
}
|
|
||||||
if (gmxData.data.responseTimeMs) {
|
|
||||||
marketDetails['Response Time'] = `${gmxData.data.responseTimeMs}ms`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add sample markets info (just count for details section)
|
// Special handling for Web3Proxy components
|
||||||
if (gmxData.data.sampleMarkets && Array.isArray(gmxData.data.sampleMarkets)) {
|
if (key === 'web3proxy' && entry.data) {
|
||||||
marketDetails['Sample Markets'] = gmxData.data.sampleMarkets.length;
|
// Handle Privy check if present
|
||||||
|
if (entry.data.privy) {
|
||||||
// If there are sample markets, add the first one's details
|
const privyData = entry.data.privy as Web3ProxyHealthDetail;
|
||||||
if (gmxData.data.sampleMarkets.length > 0) {
|
results.push({
|
||||||
const firstMarket = gmxData.data.sampleMarkets[0];
|
service,
|
||||||
if (firstMarket.indexToken) {
|
component: `${key} - Privy`,
|
||||||
marketDetails['Example Market'] = firstMarket.indexToken;
|
status: privyData.status,
|
||||||
|
duration: '',
|
||||||
|
tags: 'privy, external',
|
||||||
|
description: privyData.message || '',
|
||||||
|
details: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle GMX check if present
|
||||||
|
if (entry.data.gmx) {
|
||||||
|
const gmxData = entry.data.gmx as Web3ProxyHealthDetail;
|
||||||
|
const marketDetails: Record<string, any> = {};
|
||||||
|
|
||||||
|
// Add market count and response time if available
|
||||||
|
if (gmxData.data) {
|
||||||
|
if (gmxData.data.marketCount) {
|
||||||
|
marketDetails['Market Count'] = gmxData.data.marketCount;
|
||||||
|
}
|
||||||
|
if (gmxData.data.responseTimeMs) {
|
||||||
|
marketDetails['Response Time'] = `${gmxData.data.responseTimeMs}ms`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sample markets info (just count for details section)
|
||||||
|
if (gmxData.data.sampleMarkets && Array.isArray(gmxData.data.sampleMarkets)) {
|
||||||
|
marketDetails['Sample Markets'] = gmxData.data.sampleMarkets.length;
|
||||||
|
|
||||||
|
// If there are sample markets, add the first one's details
|
||||||
|
if (gmxData.data.sampleMarkets.length > 0) {
|
||||||
|
const firstMarket = gmxData.data.sampleMarkets[0];
|
||||||
|
if (firstMarket.indexToken) {
|
||||||
|
marketDetails['Example Market'] = firstMarket.indexToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push({
|
||||||
|
service,
|
||||||
|
component: `${key} - GMX`,
|
||||||
|
status: gmxData.status,
|
||||||
|
duration: '',
|
||||||
|
tags: 'gmx, external',
|
||||||
|
description: gmxData.message || '',
|
||||||
|
details: Object.keys(marketDetails).length > 0 ? marketDetails : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add version info if available
|
||||||
|
if (entry.data.version) {
|
||||||
|
const versionDetails: Record<string, any> = {
|
||||||
|
Version: entry.data.version
|
||||||
|
};
|
||||||
|
|
||||||
|
if (entry.data.timestamp) {
|
||||||
|
versionDetails['Last Updated'] = entry.data.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push({
|
||||||
|
service,
|
||||||
|
component: `${key} - Version`,
|
||||||
|
status: entry.status,
|
||||||
|
duration: '',
|
||||||
|
tags: 'version',
|
||||||
|
description: `Web3Proxy Version: ${entry.data.version}`,
|
||||||
|
details: versionDetails,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
results.push({
|
return results;
|
||||||
service,
|
}
|
||||||
component: `${key} - GMX`,
|
|
||||||
status: gmxData.status,
|
|
||||||
duration: '',
|
|
||||||
tags: 'gmx, external',
|
|
||||||
description: gmxData.message || '',
|
|
||||||
details: Object.keys(marketDetails).length > 0 ? marketDetails : null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add version info if available
|
// Combine all health check data for display
|
||||||
if (entry.data.version) {
|
const healthData = [
|
||||||
const versionDetails: Record<string, any> = {
|
...prepareHealthData('Managing API', apiHealth || null),
|
||||||
Version: entry.data.version
|
...prepareHealthData('Managing Worker', workerHealth || null),
|
||||||
};
|
]
|
||||||
|
|
||||||
if (entry.data.timestamp) {
|
// Define columns for the table
|
||||||
versionDetails['Last Updated'] = entry.data.timestamp;
|
const columns = React.useMemo(
|
||||||
}
|
() => [
|
||||||
|
{
|
||||||
results.push({
|
Header: 'Service',
|
||||||
service,
|
accessor: 'service',
|
||||||
component: `${key} - Version`,
|
disableSortBy: true,
|
||||||
status: entry.status,
|
disableFilters: true,
|
||||||
duration: '',
|
},
|
||||||
tags: 'version',
|
{
|
||||||
description: `Web3Proxy Version: ${entry.data.version}`,
|
Header: 'Component',
|
||||||
details: versionDetails,
|
accessor: 'component',
|
||||||
});
|
disableSortBy: true,
|
||||||
}
|
disableFilters: true,
|
||||||
}
|
},
|
||||||
});
|
{
|
||||||
|
Header: 'Status',
|
||||||
return results;
|
accessor: 'status',
|
||||||
}
|
Cell: ({value}: { value: string }) => (
|
||||||
|
<span
|
||||||
// Combine all health check data for display
|
className={`badge ${
|
||||||
const healthData = [
|
value === 'Healthy' || value === 'healthy'
|
||||||
...prepareHealthData('Managing API', apiHealth),
|
? 'badge-success'
|
||||||
...prepareHealthData('Managing Worker', workerHealth),
|
: value === 'Unreachable' || value === 'unhealthy'
|
||||||
...prepareHealthData('Web3 Proxy', web3ProxyHealth),
|
? 'badge-error'
|
||||||
]
|
: 'badge-warning'
|
||||||
|
}`}
|
||||||
// Define columns for the table
|
>
|
||||||
const columns = React.useMemo(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
Header: 'Service',
|
|
||||||
accessor: 'service',
|
|
||||||
disableSortBy: true,
|
|
||||||
disableFilters: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Component',
|
|
||||||
accessor: 'component',
|
|
||||||
disableSortBy: true,
|
|
||||||
disableFilters: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Status',
|
|
||||||
accessor: 'status',
|
|
||||||
Cell: ({ value }: { value: string }) => (
|
|
||||||
<span
|
|
||||||
className={`badge ${
|
|
||||||
value === 'Healthy' || value === 'healthy'
|
|
||||||
? 'badge-success'
|
|
||||||
: value === 'Unreachable' || value === 'unhealthy'
|
|
||||||
? 'badge-error'
|
|
||||||
: 'badge-warning'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{value.charAt(0).toUpperCase() + value.slice(1)}
|
{value.charAt(0).toUpperCase() + value.slice(1)}
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
disableFilters: true,
|
disableFilters: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Description',
|
Header: 'Description',
|
||||||
accessor: 'description',
|
accessor: 'description',
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
disableFilters: true,
|
disableFilters: true,
|
||||||
Cell: ({ value, row }: any) => (
|
Cell: ({value, row}: any) => (
|
||||||
<div>
|
<div>
|
||||||
<div>{value}</div>
|
<div>{value}</div>
|
||||||
{row.original.details && (
|
{row.original.details && (
|
||||||
<div className="text-xs mt-1 opacity-80">
|
<div className="text-xs mt-1 opacity-80">
|
||||||
{Object.entries(row.original.details).filter(([_, val]) => val !== undefined).map(
|
{Object.entries(row.original.details).filter(([_, val]) => val !== undefined).map(
|
||||||
([key, val]) => (
|
([key, val]) => (
|
||||||
<div key={key}>
|
<div key={key}>
|
||||||
<span className="font-semibold">{key}:</span> {String(val)}
|
<span className="font-semibold">{key}:</span> {String(val)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
),
|
||||||
)}
|
},
|
||||||
</div>
|
{
|
||||||
)}
|
Header: 'Duration',
|
||||||
</div>
|
accessor: 'duration',
|
||||||
),
|
disableSortBy: true,
|
||||||
},
|
disableFilters: true,
|
||||||
{
|
},
|
||||||
Header: 'Duration',
|
{
|
||||||
accessor: 'duration',
|
Header: 'Tags',
|
||||||
disableSortBy: true,
|
accessor: 'tags',
|
||||||
disableFilters: true,
|
disableSortBy: true,
|
||||||
},
|
disableFilters: true,
|
||||||
{
|
},
|
||||||
Header: 'Tags',
|
],
|
||||||
accessor: 'tags',
|
[]
|
||||||
disableSortBy: true,
|
)
|
||||||
disableFilters: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto">
|
<div className="container mx-auto">
|
||||||
<h2 className="text-xl font-bold mb-4">System Health Status</h2>
|
<h2 className="text-xl font-bold mb-4">System Health Status</h2>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<progress className="progress progress-primary w-56"></progress>
|
<progress className="progress progress-primary w-56"></progress>
|
||||||
) : (
|
) : (
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={healthData}
|
data={healthData}
|
||||||
showPagination={false}
|
showPagination={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HealthChecks
|
export default HealthChecks
|
||||||
|
|||||||
Reference in New Issue
Block a user