change(ui) - x-ray include fetch, axios and graphql
This commit is contained in:
parent
4ee54e062e
commit
5cb8283bc3
12 changed files with 497 additions and 129 deletions
|
|
@ -87,7 +87,7 @@ export default class Fetch extends React.PureComponent {
|
|||
render() {
|
||||
const { listNow } = this.props;
|
||||
const { current, currentIndex, showFetchDetails, filteredList } = this.state;
|
||||
const hasErrors = filteredList.some((r) => r.status >= 400);
|
||||
// const hasErrors = filteredList.some((r) => r.status >= 400);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SlideModal
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ function OverviewPanel(props: Props) {
|
|||
const [selectedFeatures, setSelectedFeatures] = React.useState([
|
||||
'PERFORMANCE',
|
||||
'ERRORS',
|
||||
'EVENTS',
|
||||
// 'EVENTS',
|
||||
'NETWORK',
|
||||
]);
|
||||
|
||||
const resources: any = React.useMemo(() => {
|
||||
|
|
@ -131,7 +132,10 @@ export default connect(
|
|||
}
|
||||
)(
|
||||
connectPlayer((state: any) => ({
|
||||
resourceList: state.resourceList.filter((r: any) => r.isRed() || r.isYellow()),
|
||||
resourceList: state.resourceList
|
||||
.filter((r: any) => r.isRed() || r.isYellow())
|
||||
.concat(state.fetchList.filter((i: any) => parseInt(i.status) >= 400))
|
||||
.concat(state.graphqlList.filter((i: any) => parseInt(i.status) >= 400)),
|
||||
exceptionsList: state.exceptionsList,
|
||||
eventsList: state.eventList,
|
||||
stackEventList: state.stackList,
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ const EventRow = React.memo((props: Props) => {
|
|||
!isGraph &&
|
||||
React.useMemo(() => {
|
||||
return list.map((item: any, _index: number) => {
|
||||
const spread = item.toJS ? { ...item.toJS() } : { ...item }
|
||||
return {
|
||||
...item.toJS(),
|
||||
...spread,
|
||||
left: getTimelinePosition(item.time, scale),
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,144 +7,166 @@ import { Tooltip } from 'react-tippy';
|
|||
import { TYPES as EVENT_TYPES } from 'Types/session/event';
|
||||
import StackEventModal from '../StackEventModal';
|
||||
import ErrorDetailsModal from 'App/components/Dashboard/components/Errors/ErrorDetailsModal';
|
||||
import FetchDetails from 'Shared/FetchDetailsModal';
|
||||
import GraphQLDetailsModal from 'Shared/GraphQLDetailsModal';
|
||||
|
||||
interface Props {
|
||||
pointer: any;
|
||||
type: any;
|
||||
pointer: any;
|
||||
type: any;
|
||||
}
|
||||
const TimelinePointer = React.memo((props: Props) => {
|
||||
const { showModal, hideModal } = useModal();
|
||||
const createEventClickHandler = (pointer: any, type: any) => (e: any) => {
|
||||
e.stopPropagation();
|
||||
Controls.jump(pointer.time);
|
||||
if (!type) {
|
||||
return;
|
||||
const { showModal, hideModal } = useModal();
|
||||
const createEventClickHandler = (pointer: any, type: any) => (e: any) => {
|
||||
e.stopPropagation();
|
||||
Controls.jump(pointer.time);
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'ERRORS') {
|
||||
showModal(<ErrorDetailsModal errorId={pointer.errorId} />, { right: true });
|
||||
}
|
||||
|
||||
if (type === 'EVENT') {
|
||||
showModal(<StackEventModal event={pointer} />, { right: true });
|
||||
}
|
||||
|
||||
if (type === NETWORK) {
|
||||
if (pointer.tp === 'graph_ql') {
|
||||
showModal(<GraphQLDetailsModal resource={pointer} />, { right: true });
|
||||
} else {
|
||||
showModal(<FetchDetails resource={pointer} />, { right: true });
|
||||
}
|
||||
}
|
||||
// props.toggleBottomBlock(type);
|
||||
};
|
||||
|
||||
const renderNetworkElement = (item: any) => {
|
||||
const name = item.name || '';
|
||||
return (
|
||||
<Popup
|
||||
content={
|
||||
<div className="">
|
||||
<b>{item.success ? 'Slow resource: ' : 'Missing resource:'}</b>
|
||||
<br />
|
||||
{name.length > 200 ? name.slice(0, 100) + ' ... ' + name.slice(-50) : name}
|
||||
</div>
|
||||
}
|
||||
delay={0}
|
||||
position="top"
|
||||
>
|
||||
<div onClick={createEventClickHandler(item, NETWORK)} className="cursor-pointer">
|
||||
<div className="h-3 w-3 rounded-full bg-red" />
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
if (type === 'ERRORS') {
|
||||
showModal(<ErrorDetailsModal errorId={pointer.errorId} />, { right: true });
|
||||
const renderClickRageElement = (item: any) => {
|
||||
return (
|
||||
<Popup
|
||||
content={
|
||||
<div className="">
|
||||
<b>{'Click Rage'}</b>
|
||||
</div>
|
||||
}
|
||||
delay={0}
|
||||
position="top"
|
||||
>
|
||||
<div onClick={createEventClickHandler(item, null)} className="cursor-pointer">
|
||||
<Icon className="bg-white" name="funnel/emoji-angry" color="red" size="16" />
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
if (type === 'EVENT') {
|
||||
showModal(<StackEventModal event={pointer} />, { right: true });
|
||||
const renderStackEventElement = (item: any) => {
|
||||
return (
|
||||
<Popup
|
||||
content={
|
||||
<div className="">
|
||||
<b>{'Stack Event'}</b>
|
||||
</div>
|
||||
}
|
||||
// props.toggleBottomBlock(type);
|
||||
};
|
||||
delay={0}
|
||||
position="top"
|
||||
>
|
||||
<div
|
||||
onClick={createEventClickHandler(item, 'EVENT')}
|
||||
className="cursor-pointer w-1 h-4 bg-red"
|
||||
>
|
||||
{/* <Icon className="rounded-full bg-white" name="funnel/exclamation-circle-fill" color="red" size="16" /> */}
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
const renderNetworkElement = (item: any) => {
|
||||
return (
|
||||
<Popup
|
||||
content={
|
||||
<div className="">
|
||||
<b>{item.success ? 'Slow resource: ' : 'Missing resource:'}</b>
|
||||
<br />
|
||||
{item.name.length > 200 ? (item.name.slice(0, 100) + ' ... ' + item.name.slice(-50)) : item.name}
|
||||
</div>
|
||||
}
|
||||
delay={0}
|
||||
position="top"
|
||||
>
|
||||
<div onClick={createEventClickHandler(item, NETWORK)} className="cursor-pointer">
|
||||
<div className="h-3 w-3 rounded-full bg-red" />
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
const renderClickRageElement = (item: any) => {
|
||||
return (
|
||||
<Popup
|
||||
content={
|
||||
<div className="">
|
||||
<b>{'Click Rage'}</b>
|
||||
</div>
|
||||
}
|
||||
delay={0}
|
||||
position="top"
|
||||
>
|
||||
<div onClick={createEventClickHandler(item, null)} className="cursor-pointer">
|
||||
<Icon className="bg-white" name="funnel/emoji-angry" color="red" size="16" />
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
const renderStackEventElement = (item: any) => {
|
||||
return (
|
||||
<Popup
|
||||
content={
|
||||
<div className="">
|
||||
<b>{'Stack Event'}</b>
|
||||
</div>
|
||||
}
|
||||
delay={0}
|
||||
position="top"
|
||||
>
|
||||
<div onClick={createEventClickHandler(item, 'EVENT')} className="cursor-pointer w-1 h-4 bg-red">
|
||||
{/* <Icon className="rounded-full bg-white" name="funnel/exclamation-circle-fill" color="red" size="16" /> */}
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
const renderPerformanceElement = (item: any) => {
|
||||
return (
|
||||
<Popup
|
||||
content={
|
||||
<div className="">
|
||||
<b>{item.type}</b>
|
||||
</div>
|
||||
}
|
||||
delay={0}
|
||||
position="top"
|
||||
>
|
||||
<div onClick={createEventClickHandler(item, EXCEPTIONS)} className="cursor-pointer w-1 h-4 bg-red">
|
||||
{/* <Icon className="rounded-full bg-white" name="funnel/exclamation-circle-fill" color="red" size="16" /> */}
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
const renderExceptionElement = (item: any) => {
|
||||
return (
|
||||
<Popup
|
||||
content={
|
||||
<div className="">
|
||||
<b>{'Exception'}</b>
|
||||
<br />
|
||||
<span>{item.message}</span>
|
||||
</div>
|
||||
}
|
||||
delay={0}
|
||||
position="top"
|
||||
>
|
||||
<div onClick={createEventClickHandler(item, 'ERRORS')} className="cursor-pointer">
|
||||
<Icon className="rounded-full bg-white" name="funnel/exclamation-circle-fill" color="red" size="16" />
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
const { pointer, type } = props;
|
||||
if (type === 'NETWORK') {
|
||||
return renderNetworkElement(pointer);
|
||||
}
|
||||
if (type === 'CLICKRAGE') {
|
||||
return renderClickRageElement(pointer);
|
||||
}
|
||||
if (type === 'ERRORS') {
|
||||
return renderExceptionElement(pointer);
|
||||
}
|
||||
if (type === 'EVENTS') {
|
||||
return renderStackEventElement(pointer);
|
||||
const renderPerformanceElement = (item: any) => {
|
||||
return (
|
||||
<Popup
|
||||
content={
|
||||
<div className="">
|
||||
<b>{item.type}</b>
|
||||
</div>
|
||||
}
|
||||
delay={0}
|
||||
position="top"
|
||||
>
|
||||
<div
|
||||
onClick={createEventClickHandler(item, EXCEPTIONS)}
|
||||
className="cursor-pointer w-1 h-4 bg-red"
|
||||
>
|
||||
{/* <Icon className="rounded-full bg-white" name="funnel/exclamation-circle-fill" color="red" size="16" /> */}
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
if (type === 'PERFORMANCE') {
|
||||
return renderPerformanceElement(pointer);
|
||||
const renderExceptionElement = (item: any) => {
|
||||
return (
|
||||
<Popup
|
||||
content={
|
||||
<div className="">
|
||||
<b>{'Exception'}</b>
|
||||
<br />
|
||||
<span>{item.message}</span>
|
||||
</div>
|
||||
}
|
||||
};
|
||||
return <div>{render()}</div>;
|
||||
delay={0}
|
||||
position="top"
|
||||
>
|
||||
<div onClick={createEventClickHandler(item, 'ERRORS')} className="cursor-pointer">
|
||||
<Icon
|
||||
className="rounded-full bg-white"
|
||||
name="funnel/exclamation-circle-fill"
|
||||
color="red"
|
||||
size="16"
|
||||
/>
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
const { pointer, type } = props;
|
||||
if (type === 'NETWORK') {
|
||||
return renderNetworkElement(pointer);
|
||||
}
|
||||
if (type === 'CLICKRAGE') {
|
||||
return renderClickRageElement(pointer);
|
||||
}
|
||||
if (type === 'ERRORS') {
|
||||
return renderExceptionElement(pointer);
|
||||
}
|
||||
if (type === 'EVENTS') {
|
||||
return renderStackEventElement(pointer);
|
||||
}
|
||||
|
||||
if (type === 'PERFORMANCE') {
|
||||
return renderPerformanceElement(pointer);
|
||||
}
|
||||
};
|
||||
return <div>{render()}</div>;
|
||||
});
|
||||
|
||||
export default TimelinePointer;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
import React from 'react';
|
||||
import { JSONTree, NoContent, Button, Tabs } from 'UI';
|
||||
import cn from 'classnames';
|
||||
import stl from './fetchDetails.module.css';
|
||||
import Headers from './components/Headers';
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
|
||||
const HEADERS = 'HEADERS';
|
||||
const REQUEST = 'REQUEST';
|
||||
const RESPONSE = 'RESPONSE';
|
||||
|
||||
const TABS = [HEADERS, REQUEST, RESPONSE].map((tab) => ({ text: tab, key: tab }));
|
||||
|
||||
export default class FetchDetailsModal extends React.PureComponent {
|
||||
state = { activeTab: REQUEST, tabs: [] };
|
||||
|
||||
onTabClick = (activeTab) => this.setState({ activeTab });
|
||||
|
||||
componentDidMount() {
|
||||
this.checkTabs();
|
||||
}
|
||||
|
||||
renderActiveTab = (tab) => {
|
||||
const {
|
||||
resource: { payload, response = this.props.resource.body },
|
||||
} = this.props;
|
||||
let jsonPayload,
|
||||
jsonResponse,
|
||||
requestHeaders,
|
||||
responseHeaders = undefined;
|
||||
|
||||
try {
|
||||
jsonPayload = typeof payload === 'string' ? JSON.parse(payload) : payload;
|
||||
requestHeaders = jsonPayload.headers;
|
||||
jsonPayload.body =
|
||||
typeof jsonPayload.body === 'string' ? JSON.parse(jsonPayload.body) : jsonPayload.body;
|
||||
delete jsonPayload.headers;
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
jsonResponse = typeof response === 'string' ? JSON.parse(response) : response;
|
||||
responseHeaders = jsonResponse.headers;
|
||||
jsonResponse.body =
|
||||
typeof jsonResponse.body === 'string' ? JSON.parse(jsonResponse.body) : jsonResponse.body;
|
||||
delete jsonResponse.headers;
|
||||
} catch (e) {}
|
||||
|
||||
switch (tab) {
|
||||
case REQUEST:
|
||||
return (
|
||||
<NoContent
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<AnimatedSVG name={ICONS.NO_RESULTS} size="170" />
|
||||
<div className="mt-6 text-2xl">Body is Empty.</div>
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={!payload}
|
||||
// animatedIcon="no-results"
|
||||
>
|
||||
<div>
|
||||
<div className="mt-6">
|
||||
{jsonPayload === undefined ? (
|
||||
<div className="ml-3 break-words my-3"> {payload} </div>
|
||||
) : (
|
||||
<JSONTree src={jsonPayload} collapsed={false} enableClipboard />
|
||||
)}
|
||||
</div>
|
||||
<div className="divider" />
|
||||
</div>
|
||||
</NoContent>
|
||||
);
|
||||
case RESPONSE:
|
||||
return (
|
||||
<NoContent
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<AnimatedSVG name={ICONS.NO_RESULTS} size="170" />
|
||||
<div className="mt-6 text-2xl">Body is Empty.</div>
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={!response}
|
||||
// animatedIcon="no-results"
|
||||
>
|
||||
<div>
|
||||
<div className="mt-6">
|
||||
{jsonResponse === undefined ? (
|
||||
<div className="ml-3 break-words my-3"> {response} </div>
|
||||
) : (
|
||||
<JSONTree src={jsonResponse} collapsed={false} enableClipboard />
|
||||
)}
|
||||
</div>
|
||||
<div className="divider" />
|
||||
</div>
|
||||
</NoContent>
|
||||
);
|
||||
case HEADERS:
|
||||
return <Headers requestHeaders={requestHeaders} responseHeaders={responseHeaders} />;
|
||||
}
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.resource.index === this.props.resource.index) return;
|
||||
|
||||
this.checkTabs();
|
||||
}
|
||||
|
||||
checkTabs() {
|
||||
const {
|
||||
resource: { payload, response, body },
|
||||
isResult,
|
||||
} = this.props;
|
||||
const _tabs = TABS;
|
||||
// const _tabs = TABS.filter(t => {
|
||||
// if (t.key == REQUEST && !!payload) {
|
||||
// return true
|
||||
// }
|
||||
|
||||
// if (t.key == RESPONSE && !!response) {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// return false;
|
||||
// })
|
||||
this.setState({ tabs: _tabs, activeTab: _tabs.length > 0 ? _tabs[0].key : null });
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
resource: { method, url, duration },
|
||||
nextClick,
|
||||
prevClick,
|
||||
first = false,
|
||||
last = false,
|
||||
} = this.props;
|
||||
const { activeTab, tabs } = this.state;
|
||||
|
||||
return (
|
||||
<div className="bg-white p-5 h-screen overflow-y-auto" style={{ width: '500px' }}>
|
||||
<h5 className="mb-2">{'URL'}</h5>
|
||||
<div className={cn(stl.url, 'color-gray-darkest')}>{url}</div>
|
||||
<div className="flex items-center mt-4">
|
||||
<div className="w-4/12">
|
||||
<div className="font-medium mb-2">Method</div>
|
||||
<div>{method}</div>
|
||||
</div>
|
||||
<div className="w-4/12">
|
||||
<div className="font-medium mb-2">Duration</div>
|
||||
<div>{parseInt(duration)} ms</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6">
|
||||
<div>
|
||||
<Tabs tabs={tabs} active={activeTab} onClick={this.onTabClick} border={true} />
|
||||
<div style={{ height: 'calc(100vh - 314px)', overflowY: 'auto' }}>
|
||||
{this.renderActiveTab(activeTab)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <div className="flex justify-between absolute bottom-0 left-0 right-0 p-3 border-t bg-white">
|
||||
<Button variant="outline" onClick={prevClick} disabled={first}>
|
||||
Prev
|
||||
</Button>
|
||||
<Button variant="outline" onClick={nextClick} disabled={last}>
|
||||
Next
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import React from 'react'
|
||||
import { NoContent, TextEllipsis } from 'UI'
|
||||
import stl from './headers.module.css'
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
|
||||
function Headers(props) {
|
||||
return (
|
||||
<div>
|
||||
<NoContent
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<AnimatedSVG name={ICONS.NO_RESULTS} size="170" />
|
||||
<div className="mt-6 text-2xl">No data available.</div>
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={ !props.requestHeaders && !props.responseHeaders }
|
||||
// animatedIcon="no-results"
|
||||
>
|
||||
{ props.requestHeaders && (
|
||||
<>
|
||||
<div className="mb-4 mt-4">
|
||||
<div className="my-2 font-medium">Request Headers</div>
|
||||
{
|
||||
Object.keys(props.requestHeaders).map(h => (
|
||||
<div className={stl.row}>
|
||||
<span className="mr-2 font-medium">{h}:</span>
|
||||
<span>{props.requestHeaders[h]}</span>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<hr />
|
||||
</>
|
||||
)}
|
||||
|
||||
{ props.responseHeaders && (
|
||||
<div className="mt-4">
|
||||
<div className="my-2 font-medium">Response Headers</div>
|
||||
{
|
||||
Object.keys(props.responseHeaders).map(h => (
|
||||
<div className={stl.row}>
|
||||
<span className="mr-2 font-medium">{h}:</span>
|
||||
<span>{props.responseHeaders[h]}</span>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</NoContent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Headers;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
.row {
|
||||
/* display: flex; */
|
||||
padding: 5px 0px;
|
||||
font-size: 13px;
|
||||
word-break: break-all;
|
||||
/* padding-left: 20px; */
|
||||
&:hover {
|
||||
background-color: $active-blue;
|
||||
}
|
||||
|
||||
& div:last-child {
|
||||
max-width: 80%;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './Headers'
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
|
||||
.url {
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
background-color: $gray-lightest;
|
||||
/* border: solid thin $gray-light; */
|
||||
/* max-width: 90%; */
|
||||
word-break: break-all;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 3px 8px;
|
||||
border-radius: 12px;
|
||||
/*border: 1px solid $gray-light;*/
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './FetchDetailsModal';
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
import React from 'react';
|
||||
import { JSONTree, Button } from 'UI';
|
||||
import cn from 'classnames';
|
||||
|
||||
interface Props {
|
||||
resource: any;
|
||||
}
|
||||
function GraphQLDetailsModal(props: Props) {
|
||||
const {
|
||||
resource: { variables, response, duration, operationKind, operationName },
|
||||
// nextClick,
|
||||
// prevClick,
|
||||
// first = false,
|
||||
// last = false,
|
||||
} = props;
|
||||
|
||||
let jsonVars = undefined;
|
||||
let jsonResponse = undefined;
|
||||
try {
|
||||
jsonVars = JSON.parse(variables);
|
||||
} catch (e) {}
|
||||
try {
|
||||
jsonResponse = JSON.parse(response);
|
||||
} catch (e) {}
|
||||
const dataClass = cn('p-2 bg-gray-lightest rounded color-gray-darkest');
|
||||
|
||||
return (
|
||||
<div className="p-5 bg-white h-screen overflow-y-auto" style={{ width: '500px' }}>
|
||||
<h5 className="mb-2">{'Operation Name'}</h5>
|
||||
<div className={dataClass}>{operationName}</div>
|
||||
|
||||
<div className="flex items-center gap-4 mt-4">
|
||||
<div className="w-6/12">
|
||||
<div className="mb-2">Operation Kind</div>
|
||||
<div className={dataClass}>{operationKind}</div>
|
||||
</div>
|
||||
<div className="w-6/12">
|
||||
<div className="mb-2">Duration</div>
|
||||
<div className={dataClass}>{duration ? parseInt(duration) : '???'} ms</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ height: 'calc(100vh - 314px)', overflowY: 'auto' }}>
|
||||
<div>
|
||||
<div className="flex justify-between items-start mt-6 mb-2">
|
||||
<h5 className="mt-1 mr-1">{'Variables'}</h5>
|
||||
</div>
|
||||
<div className={dataClass}>
|
||||
{jsonVars === undefined ? variables : <JSONTree src={jsonVars} />}
|
||||
</div>
|
||||
<div className="divider" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex justify-between items-start mt-6 mb-2">
|
||||
<h5 className="mt-1 mr-1">{'Response'}</h5>
|
||||
</div>
|
||||
<div className={dataClass}>
|
||||
{jsonResponse === undefined ? response : <JSONTree src={jsonResponse} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <div className="flex justify-between absolute bottom-0 left-0 right-0 p-3 border-t bg-white">
|
||||
<Button variant="outline" onClick={prevClick} disabled={first}>
|
||||
Prev
|
||||
</Button>
|
||||
<Button variant="outline" onClick={nextClick} disabled={last}>
|
||||
Next
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GraphQLDetailsModal;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './GraphQLDetailsModal';
|
||||
Loading…
Add table
Reference in a new issue