1- /**
2- * Explain Panel Component
3- *
4- * Main panel component for PostgreSQL EXPLAIN visualization.
5- * Orchestrates data extraction, validation, and rendering.
6- */
7-
8- import React , { useMemo } from "react" ;
1+ import React from "react" ;
92import { PanelProps } from "@grafana/data" ;
103import { PanelOptions } from "../types/panel-options" ;
114import {
@@ -19,76 +12,39 @@ import { validatePlan } from "../services/planValidator";
1912import { VueMount } from "./VueMount" ;
2013import { ErrorBoundary } from "./ErrorBoundary" ;
2114import { ErrorDisplay } from "./ErrorDisplay" ;
22- import { LoadingSpinner } from "./LoadingSpinner" ;
23- import { logDebug , logError } from "../utils/logger" ;
24-
25- const CONTEXT = "ExplainPanel" ;
2615
2716interface Props extends PanelProps < PanelOptions > { }
2817
29- /**
30- * ExplainPanel
31- *
32- * Main panel component that:
33- * 1. Extracts EXPLAIN data from DataFrame
34- * 2. Validates JSON structure
35- * 3. Renders visualization using Vue/PEV2
36- * 4. Handles errors gracefully
37- */
3818export const ExplainPanel : React . FC < Props > = ( {
3919 options,
4020 data,
4121 width,
4222 height,
4323} ) => {
44- logDebug ( CONTEXT , "Rendering ExplainPanel" , {
45- options,
46- seriesCount : data . series . length ,
47- width,
48- height,
49- } ) ;
50-
51- // Process data and handle errors
52- const result = useMemo ( ( ) => {
53- try {
54- // Extract plan data from DataFrame
55- const planData = extractPlanData ( data . series , options . planFieldName ) ;
56-
57- // Handle no data case
58- if ( ! planData ) {
59- const errorState : ErrorState = {
60- type : ErrorType . NO_DATA ,
61- message : "No data available" ,
62- resolutionHint :
63- "Configure a query to return EXPLAIN output. Use EXPLAIN (FORMAT JSON) SELECT ... or EXPLAIN SELECT ..." ,
64- } ;
65- return { type : "error" as const , error : errorState } ;
66- }
67-
68- // Validate and parse plan
24+ let result : { type : "success" ; plan : any } | { type : "error" ; error : ErrorState } ;
25+
26+ try {
27+ const planData = extractPlanData ( data . series , options . planFieldName ) ;
28+
29+ if ( ! planData ) {
30+ const errorState : ErrorState = {
31+ type : ErrorType . NO_DATA ,
32+ message : "No data available" ,
33+ resolutionHint :
34+ "Configure a query to return EXPLAIN output. Use EXPLAIN (FORMAT JSON) SELECT ... or EXPLAIN SELECT ..." ,
35+ } ;
36+ result = { type : "error" as const , error : errorState } ;
37+ } else {
6938 const plan = validatePlan ( planData ) ;
70-
71- logDebug ( CONTEXT , "Plan extracted and validated successfully" ) ;
72-
73- return { type : "success" as const , plan } ;
74- } catch ( error ) {
75- logError ( CONTEXT , "Error processing plan data" , error ) ;
76-
77- // Convert error to ErrorState
78- const errorState =
79- error instanceof PlanError ? error . toErrorState ( ) : toErrorState ( error ) ;
80-
81- return { type : "error" as const , error : errorState } ;
39+ result = { type : "success" as const , plan } ;
8240 }
83- } , [ data . series , options . planFieldName ] ) ;
41+ } catch ( error ) {
42+ const errorState =
43+ error instanceof PlanError ? error . toErrorState ( ) : toErrorState ( error ) ;
44+ result = { type : "error" as const , error : errorState } ;
45+ }
8446
85- // Render error state
8647 if ( result . type === "error" ) {
87- // For NO_DATA, show info message instead of error
88- if ( result . error . type === ErrorType . NO_DATA ) {
89- return < ErrorDisplay error = { result . error } /> ;
90- }
91-
9248 return < ErrorDisplay error = { result . error } /> ;
9349 }
9450
0 commit comments