@@ -12,18 +12,16 @@ import {
1212 PanelLeftCloseIcon ,
1313 PanelLeftOpenIcon ,
1414 PlayIcon ,
15- PlusCircleIcon ,
1615 PowerIcon ,
1716 PowerOffIcon ,
1817 SquarePenIcon ,
19- TrashIcon ,
2018} from 'lucide-react' ;
2119import { compressToEncodedURIComponent } from 'lz-string' ;
2220import * as monaco from 'monaco-editor/esm/vs/editor/editor.api' ;
2321import { toast } from 'sonner' ;
2422import { z } from 'zod' ;
25- import { Input } from '@/components/ui/input' ;
2623import { Tooltip , TooltipContent , TooltipTrigger } from '@/components/ui/tooltip' ;
24+ import { QueryPlanSchema } from '@/lib/query-plan/schema' ;
2725import { DropdownMenuTrigger } from '@radix-ui/react-dropdown-menu' ;
2826import { useForm } from '@tanstack/react-form' ;
2927import type {
@@ -32,18 +30,10 @@ import type {
3230 LaboratoryHistorySubscription ,
3331} from '../../lib/history' ;
3432import type { LaboratoryOperation } from '../../lib/operations' ;
35- import { QueryPlanTree , renderQueryPlan } from '../../lib/query-plan' ;
33+ import { QueryPlanTree , renderQueryPlan } from '../../lib/query-plan/utils ' ;
3634import { cn } from '../../lib/utils' ;
3735import { Badge } from '../ui/badge' ;
3836import { Button } from '../ui/button' ;
39- import {
40- Combobox ,
41- ComboboxContent ,
42- ComboboxEmpty ,
43- ComboboxInput ,
44- ComboboxItem ,
45- ComboboxList ,
46- } from '../ui/combobox' ;
4737import {
4838 Dialog ,
4939 DialogClose ,
@@ -93,134 +83,13 @@ const Variables = (props: { operation?: LaboratoryOperation | null; isReadOnly?:
9383 ) ;
9484} ;
9585
96- const HEADERS_SUGGESTIONS = [
97- 'Authorization' ,
98- 'Content-Type' ,
99- 'Accept' ,
100- 'User-Agent' ,
101- 'X-Requested-With' ,
102- 'X-CSRF-Token' ,
103- ] ;
104-
10586const Headers = ( props : { operation ?: LaboratoryOperation | null ; isReadOnly ?: boolean } ) => {
10687 const { activeOperation, updateActiveOperation } = useLaboratory ( ) ;
10788
10889 const operation = useMemo ( ( ) => {
10990 return props . operation ?? activeOperation ?? null ;
11091 } , [ props . operation , activeOperation ] ) ;
11192
112- // const [headers, setHeaders] = useState<[string, string][]>(() => {
113- // return Object.entries(JSON.parse(operation?.headers ?? '{}'));
114- // });
115-
116- // const updateHeaderKey = useCallback((index: number, newKey: string, value: string) => {
117- // setHeaders(prev => {
118- // const newHeaders = [...prev];
119- // newHeaders[index] = [newKey, value];
120- // return newHeaders;
121- // });
122- // }, []);
123-
124- // const updateHeaderValue = useCallback((index: number, value: string) => {
125- // setHeaders(prev => {
126- // const newHeaders = [...prev];
127- // newHeaders[index] = [newHeaders[index][0], value];
128- // return newHeaders;
129- // });
130- // }, []);
131-
132- // const deleteHeader = useCallback((index: number) => {
133- // setHeaders(prev => {
134- // const newHeaders = [...prev];
135- // newHeaders.splice(index, 1);
136- // return newHeaders;
137- // });
138- // }, []);
139-
140- // const addHeader = useCallback(() => {
141- // setHeaders(prev => [...prev, ['', '']]);
142- // }, []);
143-
144- // useEffect(() => {
145- // const result = JSON.stringify(Object.fromEntries(headers.filter(([key]) => !!key)));
146-
147- // if (result === operation?.headers) {
148- // return;
149- // }
150-
151- // updateActiveOperation({
152- // headers: JSON.stringify(Object.fromEntries(headers)),
153- // });
154- // }, [headers, updateActiveOperation, operation?.headers]);
155-
156- // console.log(headers.some(header2 => header2[0].toLowerCase() === 'Authorization'.toLowerCase()));
157-
158- // return (
159- // <div className="grid size-full grid-rows-[1fr_auto]">
160- // <div className="grid gap-2 overflow-y-auto p-3">
161- // {headers.map((header, index, arr) => (
162- // <div key={header[0]} className="grid grid-cols-[1fr_1fr_auto] gap-2">
163- // <Combobox
164- // items={HEADERS_SUGGESTIONS}
165- // value={header[0]}
166- // onValueChange={newKey => {
167- // updateHeaderKey(index, newKey ?? '', header[1]);
168- // }}
169- // >
170- // <ComboboxInput
171- // placeholder="Header key"
172- // aria-invalid={arr.some(
173- // header2 =>
174- // header2 !== header && header2[0].toLowerCase() === header[0].toLowerCase(),
175- // )}
176- // />
177- // <ComboboxContent className="w-full">
178- // <ComboboxList>
179- // {HEADERS_SUGGESTIONS.map(item => (
180- // <ComboboxItem key={item} value={item}>
181- // {item}
182- // </ComboboxItem>
183- // ))}
184- // </ComboboxList>
185- // </ComboboxContent>
186- // </Combobox>
187- // <Input
188- // defaultValue={`${header[1]}`}
189- // placeholder="Value"
190- // onChange={e => {
191- // updateHeaderValue(index, e.target.value);
192- // }}
193- // />
194- // <Button
195- // variant="destructive"
196- // size="icon"
197- // className="size-9 rounded-sm"
198- // disabled={index === 0}
199- // onClick={() => {
200- // deleteHeader(index);
201- // }}
202- // >
203- // <TrashIcon className="size-4" />
204- // </Button>
205- // </div>
206- // ))}
207- // </div>
208- // <div className="border-t p-3">
209- // <Button
210- // variant="link"
211- // size="sm"
212- // className="w-full"
213- // onClick={() => {
214- // addHeader();
215- // }}
216- // >
217- // <PlusCircleIcon className="size-4" />
218- // Add header
219- // </Button>
220- // </div>
221- // </div>
222- // );
223-
22493 return (
22594 < Editor
22695 uri = { monaco . Uri . file ( 'headers.json' ) }
@@ -320,6 +189,18 @@ export const ResponsePreflight = ({ historyItem }: { historyItem?: LaboratoryHis
320189export const ResponseQueryPlan = ( { historyItem } : { historyItem ?: LaboratoryHistory | null } ) => {
321190 const [ mode , setMode ] = useState < 'text' | 'visual' > ( 'text' ) ;
322191
192+ const queryPlan = useMemo ( ( ) => {
193+ const queryPlan =
194+ JSON . parse ( ( historyItem as LaboratoryHistoryRequest ) ?. response ?? '{}' ) . extensions
195+ ?. queryPlan ?? { } ;
196+
197+ if ( ! queryPlan ) {
198+ return null ;
199+ }
200+
201+ return QueryPlanSchema . safeParse ( queryPlan ) . success ? queryPlan : null ;
202+ } , [ historyItem ] ) ;
203+
323204 return (
324205 < div className = "relative size-full" >
325206 < ToggleGroup
@@ -339,18 +220,10 @@ export const ResponseQueryPlan = ({ historyItem }: { historyItem?: LaboratoryHis
339220 </ ToggleGroupItem >
340221 </ ToggleGroup >
341222 { mode === 'visual' ? (
342- < QueryPlanTree
343- plan = {
344- JSON . parse ( ( historyItem as LaboratoryHistoryRequest ) ?. response ?? '{}' ) . extensions
345- ?. queryPlan ?? { }
346- }
347- />
223+ < QueryPlanTree plan = { queryPlan } />
348224 ) : (
349225 < Editor
350- value = { renderQueryPlan (
351- JSON . parse ( ( historyItem as LaboratoryHistoryRequest ) ?. response ?? '{}' ) . extensions
352- ?. queryPlan ?? { } ,
353- ) }
226+ value = { renderQueryPlan ( queryPlan ) }
354227 defaultLanguage = "graphql"
355228 theme = "hive-laboratory"
356229 options = { { readOnly : true } }
@@ -444,12 +317,20 @@ export const Response = ({ historyItem }: { historyItem?: LaboratoryHistoryReque
444317 ) ;
445318 } , [ historyItem ] ) ;
446319
447- const hasQueryPlan = useMemo ( ( ) => {
320+ const hasValidQueryPlan = useMemo ( ( ) => {
448321 if ( ! historyItem ) {
449322 return false ;
450323 }
451324
452- return ! ! JSON . parse ( historyItem . response ) . extensions ?. queryPlan ;
325+ const queryPlan = JSON . parse ( historyItem . response ) . extensions ?. queryPlan ;
326+
327+ if ( ! queryPlan ) {
328+ return false ;
329+ }
330+
331+ console . log ( QueryPlanSchema . safeParse ( queryPlan ) . error ?. message ) ;
332+
333+ return QueryPlanSchema . safeParse ( queryPlan ) . success ;
453334 } , [ historyItem ?. response ] ) ;
454335
455336 return (
@@ -492,7 +373,7 @@ export const Response = ({ historyItem }: { historyItem?: LaboratoryHistoryReque
492373 < TabsTrigger value = "response" className = "grow-0 rounded-sm" >
493374 Response
494375 </ TabsTrigger >
495- { hasQueryPlan && (
376+ { hasValidQueryPlan && (
496377 < TabsTrigger value = "query-plan" className = "grow-0 rounded-sm" >
497378 Query Plan
498379 </ TabsTrigger >
0 commit comments