From de70674c0e8c50efb15ec0315cc1874ab4176bc5 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Fri, 10 Oct 2025 02:21:59 +0700 Subject: [PATCH] Update front for sql monitoring --- .../sqlmonitoring/sqlMonitoring.tsx | 247 ++++++++++++++---- 1 file changed, 196 insertions(+), 51 deletions(-) diff --git a/src/Managing.WebApp/src/pages/settingsPage/sqlmonitoring/sqlMonitoring.tsx b/src/Managing.WebApp/src/pages/settingsPage/sqlmonitoring/sqlMonitoring.tsx index 66a6750b..c8971164 100644 --- a/src/Managing.WebApp/src/pages/settingsPage/sqlmonitoring/sqlMonitoring.tsx +++ b/src/Managing.WebApp/src/pages/settingsPage/sqlmonitoring/sqlMonitoring.tsx @@ -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 - }> = [] + 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 + } + + 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]) + + // Prepare context statistics data for table + const contextStatsData = React.useMemo(() => { + if (!statistics?.contextStats) return [] - // 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, - }) - }) - } - - // 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 stats + 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 }) => ( +
+ {value} +
+ ), + disableSortBy: true, + disableFilters: true, + }, + { + Header: 'Executions', + accessor: 'executionCount', + Cell: ({ value }: { value: number }) => { + const numValue = Number(value) || 0 + return ( + {numValue} + ) + }, + disableSortBy: true, + disableFilters: true, + }, + { + Header: 'Per Minute', + accessor: 'executionsPerMinute', + Cell: ({ value }: { value: number }) => { + const numValue = Number(value) || 0 + return ( + 20 ? 'badge-warning' : 'badge-info'}`}> + {numValue.toFixed(1)} + + ) + }, + disableSortBy: true, + disableFilters: true, + }, + { + Header: 'Avg Time (ms)', + accessor: 'averageExecutionTime', + Cell: ({ value }: { value: number }) => { + const numValue = Number(value) || 0 + return ( + 1000 ? 'badge-error' : numValue > 500 ? 'badge-warning' : 'badge-success'}`}> + {numValue.toFixed(0)} + + ) + }, + disableSortBy: true, + disableFilters: true, + }, + { + Header: 'Status', + accessor: 'isActive', + Cell: ({ value }: { value: boolean }) => ( + + {value ? 'Active' : 'Inactive'} + + ), + disableSortBy: true, + disableFilters: true, + }, + { + Header: 'Last Execution', + accessor: 'lastExecution', + Cell: ({ value }: { value: string }) => ( + + {value ? new Date(value).toLocaleString() : 'N/A'} + + ), + disableSortBy: true, + disableFilters: true, + }, + ], + [] + ) + + // Define columns for context stats table + const contextStatsColumns = React.useMemo( + () => [ + { + Header: 'Query Pattern', + accessor: 'queryPattern', + Cell: ({ value }: { value: string }) => ( +
+ {value} +
+ ), + disableSortBy: true, + disableFilters: true, + }, + { + Header: 'Execution Count', + accessor: 'executionCount', + Cell: ({ value }: { value: number }) => { + const numValue = Number(value) || 0 + return ( + {numValue} + ) + }, disableSortBy: true, disableFilters: true, }, { Header: 'Timestamp', accessor: 'timestamp', + Cell: ({ value }: { value: string }) => ( + + {new Date(value).toLocaleString()} + + ), disableSortBy: true, disableFilters: true, }, @@ -279,22 +394,52 @@ const SqlMonitoring: React.FC = () => { - {/* Statistics Section */} + {/* Loop Detection Statistics */}
-

Query Statistics

- {statisticsData.length === 0 ? ( -
- - +

Loop Detection Statistics

+

+ Detailed performance metrics for tracked queries with loop detection +

+ {loopDetectionData.length === 0 ? ( +
+ + - No statistics available yet + No loop detection data available yet
) : (
+ + )} + + + + {/* Context Statistics */} +
+
+

Query Execution Counts

+

+ Simple execution count tracking for all query patterns +

+ {contextStatsData.length === 0 ? ( +
+ + + + No context statistics available yet +
+ ) : ( +
+