Update front for sql monitoring
This commit is contained in:
@@ -16,42 +16,55 @@ const SqlMonitoring: React.FC = () => {
|
||||
|
||||
const isLoading = isLoadingStats || isLoadingAlerts || isLoadingHealth
|
||||
|
||||
// Prepare statistics data for table
|
||||
const statisticsData = React.useMemo(() => {
|
||||
if (!statistics) return []
|
||||
// Prepare loop detection statistics data for table
|
||||
const loopDetectionData = React.useMemo(() => {
|
||||
if (!statistics?.loopDetectionStats) return []
|
||||
|
||||
const stats: Array<{
|
||||
type: string
|
||||
key: string
|
||||
value: string
|
||||
timestamp: string
|
||||
}> = []
|
||||
|
||||
// Add loop detection stats
|
||||
if (statistics.loopDetectionStats) {
|
||||
Object.entries(statistics.loopDetectionStats).forEach(([key, value]) => {
|
||||
stats.push({
|
||||
type: 'Loop Detection',
|
||||
key,
|
||||
value: typeof value === 'object' ? JSON.stringify(value) : String(value),
|
||||
timestamp: statistics.timestamp,
|
||||
})
|
||||
})
|
||||
return Object.entries(statistics.loopDetectionStats).map(([key, stat]) => {
|
||||
// Helper function to safely convert time values to milliseconds
|
||||
const getTimeInMs = (timeValue: any): number => {
|
||||
if (typeof timeValue === 'number') return timeValue
|
||||
if (typeof timeValue === 'string') {
|
||||
// Handle TimeSpan serialization from C# (e.g., "00:00:01.234")
|
||||
const match = timeValue.match(/(\d+):(\d+):(\d+)\.(\d+)/)
|
||||
if (match) {
|
||||
const [, hours, minutes, seconds, milliseconds] = match
|
||||
return (parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseInt(seconds)) * 1000 + parseInt(milliseconds)
|
||||
}
|
||||
// Try to parse as number
|
||||
const parsed = parseFloat(timeValue)
|
||||
return isNaN(parsed) ? 0 : parsed
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Add context stats
|
||||
if (statistics.contextStats) {
|
||||
Object.entries(statistics.contextStats).forEach(([key, value]) => {
|
||||
stats.push({
|
||||
type: 'Context',
|
||||
key,
|
||||
value: String(value),
|
||||
timestamp: statistics.timestamp,
|
||||
})
|
||||
})
|
||||
return {
|
||||
id: key,
|
||||
repository: stat.repositoryName || 'Unknown',
|
||||
method: stat.methodName || 'Unknown',
|
||||
queryPattern: stat.queryPattern || 'Unknown',
|
||||
executionCount: Number(stat.executionCount) || 0,
|
||||
executionsPerMinute: Number(stat.executionsPerMinute) || 0,
|
||||
averageExecutionTime: getTimeInMs(stat.averageExecutionTime),
|
||||
minExecutionTime: getTimeInMs(stat.minExecutionTime),
|
||||
maxExecutionTime: getTimeInMs(stat.maxExecutionTime),
|
||||
firstExecution: stat.firstExecution || '',
|
||||
lastExecution: stat.lastExecution || '',
|
||||
isActive: Boolean(stat.isActive),
|
||||
}
|
||||
})
|
||||
}, [statistics])
|
||||
|
||||
return stats
|
||||
// Prepare context statistics data for table
|
||||
const contextStatsData = React.useMemo(() => {
|
||||
if (!statistics?.contextStats) return []
|
||||
|
||||
return Object.entries(statistics.contextStats).map(([queryPattern, count]) => ({
|
||||
id: queryPattern,
|
||||
queryPattern: queryPattern.length > 50 ? `${queryPattern.substring(0, 50)}...` : queryPattern,
|
||||
executionCount: count,
|
||||
timestamp: statistics.timestamp,
|
||||
}))
|
||||
}, [statistics])
|
||||
|
||||
// Prepare alerts data for table
|
||||
@@ -68,30 +81,132 @@ const SqlMonitoring: React.FC = () => {
|
||||
}))
|
||||
}, [alerts])
|
||||
|
||||
// Define columns for statistics table
|
||||
const statisticsColumns = React.useMemo(
|
||||
// Define columns for loop detection table
|
||||
const loopDetectionColumns = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: 'Type',
|
||||
accessor: 'type',
|
||||
Header: 'Repository',
|
||||
accessor: 'repository',
|
||||
disableSortBy: true,
|
||||
disableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Key',
|
||||
accessor: 'key',
|
||||
Header: 'Method',
|
||||
accessor: 'method',
|
||||
disableSortBy: true,
|
||||
disableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Value',
|
||||
accessor: 'value',
|
||||
Header: 'Query Pattern',
|
||||
accessor: 'queryPattern',
|
||||
Cell: ({ value }: { value: string }) => (
|
||||
<div className="max-w-xs truncate" title={value}>
|
||||
{value}
|
||||
</div>
|
||||
),
|
||||
disableSortBy: true,
|
||||
disableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Executions',
|
||||
accessor: 'executionCount',
|
||||
Cell: ({ value }: { value: number }) => {
|
||||
const numValue = Number(value) || 0
|
||||
return (
|
||||
<span className="badge badge-primary">{numValue}</span>
|
||||
)
|
||||
},
|
||||
disableSortBy: true,
|
||||
disableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Per Minute',
|
||||
accessor: 'executionsPerMinute',
|
||||
Cell: ({ value }: { value: number }) => {
|
||||
const numValue = Number(value) || 0
|
||||
return (
|
||||
<span className={`badge ${numValue > 20 ? 'badge-warning' : 'badge-info'}`}>
|
||||
{numValue.toFixed(1)}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
disableSortBy: true,
|
||||
disableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Avg Time (ms)',
|
||||
accessor: 'averageExecutionTime',
|
||||
Cell: ({ value }: { value: number }) => {
|
||||
const numValue = Number(value) || 0
|
||||
return (
|
||||
<span className={`badge ${numValue > 1000 ? 'badge-error' : numValue > 500 ? 'badge-warning' : 'badge-success'}`}>
|
||||
{numValue.toFixed(0)}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
disableSortBy: true,
|
||||
disableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
accessor: 'isActive',
|
||||
Cell: ({ value }: { value: boolean }) => (
|
||||
<span className={`badge ${value ? 'badge-success' : 'badge-neutral'}`}>
|
||||
{value ? 'Active' : 'Inactive'}
|
||||
</span>
|
||||
),
|
||||
disableSortBy: true,
|
||||
disableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Last Execution',
|
||||
accessor: 'lastExecution',
|
||||
Cell: ({ value }: { value: string }) => (
|
||||
<span className="text-xs">
|
||||
{value ? new Date(value).toLocaleString() : 'N/A'}
|
||||
</span>
|
||||
),
|
||||
disableSortBy: true,
|
||||
disableFilters: true,
|
||||
},
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
// Define columns for context stats table
|
||||
const contextStatsColumns = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: 'Query Pattern',
|
||||
accessor: 'queryPattern',
|
||||
Cell: ({ value }: { value: string }) => (
|
||||
<div className="max-w-md truncate font-mono text-xs" title={value}>
|
||||
{value}
|
||||
</div>
|
||||
),
|
||||
disableSortBy: true,
|
||||
disableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Execution Count',
|
||||
accessor: 'executionCount',
|
||||
Cell: ({ value }: { value: number }) => {
|
||||
const numValue = Number(value) || 0
|
||||
return (
|
||||
<span className="badge badge-secondary">{numValue}</span>
|
||||
)
|
||||
},
|
||||
disableSortBy: true,
|
||||
disableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Timestamp',
|
||||
accessor: 'timestamp',
|
||||
Cell: ({ value }: { value: string }) => (
|
||||
<span className="text-xs">
|
||||
{new Date(value).toLocaleString()}
|
||||
</span>
|
||||
),
|
||||
disableSortBy: true,
|
||||
disableFilters: true,
|
||||
},
|
||||
@@ -279,22 +394,52 @@ const SqlMonitoring: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Statistics Section */}
|
||||
{/* Loop Detection Statistics */}
|
||||
<div className="card bg-base-200">
|
||||
<div className="card-body">
|
||||
<h3 className="card-title text-lg">Query Statistics</h3>
|
||||
{statisticsData.length === 0 ? (
|
||||
<div className="alert alert-warning">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
||||
<h3 className="card-title text-lg">Loop Detection Statistics</h3>
|
||||
<p className="text-sm text-base-content/70 mb-4">
|
||||
Detailed performance metrics for tracked queries with loop detection
|
||||
</p>
|
||||
{loopDetectionData.length === 0 ? (
|
||||
<div className="alert alert-info">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="stroke-current shrink-0 w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
<span>No statistics available yet</span>
|
||||
<span>No loop detection data available yet</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
<Table
|
||||
columns={statisticsColumns}
|
||||
data={statisticsData}
|
||||
columns={loopDetectionColumns}
|
||||
data={loopDetectionData}
|
||||
showPagination={true}
|
||||
showTotal={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Context Statistics */}
|
||||
<div className="card bg-base-200">
|
||||
<div className="card-body">
|
||||
<h3 className="card-title text-lg">Query Execution Counts</h3>
|
||||
<p className="text-sm text-base-content/70 mb-4">
|
||||
Simple execution count tracking for all query patterns
|
||||
</p>
|
||||
{contextStatsData.length === 0 ? (
|
||||
<div className="alert alert-info">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="stroke-current shrink-0 w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
<span>No context statistics available yet</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
<Table
|
||||
columns={contextStatsColumns}
|
||||
data={contextStatsData}
|
||||
showPagination={true}
|
||||
showTotal={false}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user