change(ui) - console panel changes and other improvements
This commit is contained in:
parent
a9cf0cfdca
commit
c11e3b9069
12 changed files with 144 additions and 88 deletions
|
|
@ -7,7 +7,8 @@ import { LEVEL } from 'Types/session/log';
|
||||||
import Autoscroll from '../Autoscroll';
|
import Autoscroll from '../Autoscroll';
|
||||||
import BottomBlock from '../BottomBlock';
|
import BottomBlock from '../BottomBlock';
|
||||||
import stl from './console.module.css';
|
import stl from './console.module.css';
|
||||||
import { Duration } from 'luxon';
|
import ConsoleRow from './ConsoleRow';
|
||||||
|
// import { Duration } from 'luxon';
|
||||||
|
|
||||||
const ALL = 'ALL';
|
const ALL = 'ALL';
|
||||||
const INFO = 'INFO';
|
const INFO = 'INFO';
|
||||||
|
|
@ -93,35 +94,24 @@ export default class ConsoleContent extends React.PureComponent {
|
||||||
/>
|
/>
|
||||||
</BottomBlock.Header>
|
</BottomBlock.Header>
|
||||||
<BottomBlock.Content>
|
<BottomBlock.Content>
|
||||||
<NoContent title={
|
<NoContent
|
||||||
<div className="capitalize flex items-center mt-16">
|
title={
|
||||||
|
<div className="capitalize flex items-center mt-16">
|
||||||
<Icon name="info-circle" className="mr-2" size="18" />
|
<Icon name="info-circle" className="mr-2" size="18" />
|
||||||
No Data
|
No Data
|
||||||
</div>
|
</div>
|
||||||
} size="small" show={filtered.length === 0}>
|
}
|
||||||
|
size="small"
|
||||||
|
show={filtered.length === 0}
|
||||||
|
>
|
||||||
<Autoscroll autoScrollTo={Math.max(lastIndex, 0)}>
|
<Autoscroll autoScrollTo={Math.max(lastIndex, 0)}>
|
||||||
{filtered.map((l, index) => (
|
{filtered.map((l) => (
|
||||||
<div
|
<ConsoleRow
|
||||||
className={cn(stl.line, 'flex py-2 px-4', {
|
log={l}
|
||||||
info: !l.isYellow() && !l.isRed(),
|
jump={jump}
|
||||||
warn: l.isYellow(),
|
iconProps={getIconProps(l.level)}
|
||||||
error: l.isRed(),
|
renderWithNL={renderWithNL}
|
||||||
[stl.activeRow]: lastIndex === index,
|
/>
|
||||||
// [stl.inactiveRow]: index > lastIndex,
|
|
||||||
'cursor-pointer': !isResult,
|
|
||||||
})}
|
|
||||||
onClick={() => !isResult && jump(l.time)}
|
|
||||||
>
|
|
||||||
<div className={cn(stl.timestamp)}>
|
|
||||||
<Icon size="14" className={stl.icon} {...getIconProps(l.level)} />
|
|
||||||
</div>
|
|
||||||
{/* <div className={cn(stl.timestamp, {})}>
|
|
||||||
{Duration.fromMillis(l.time).toFormat('mm:ss.SSS')}
|
|
||||||
</div> */}
|
|
||||||
<div key={l.key} className={cn('')} data-scroll-item={l.isRed()}>
|
|
||||||
<div className={stl.message}>{renderWithNL(l.value)}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</Autoscroll>
|
</Autoscroll>
|
||||||
</NoContent>
|
</NoContent>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import stl from '../console.module.css';
|
||||||
|
import { Icon } from 'UI';
|
||||||
|
import JumpButton from 'Shared/DevTools/JumpButton';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
log: any;
|
||||||
|
iconProps: any;
|
||||||
|
jump?: any;
|
||||||
|
renderWithNL?: any;
|
||||||
|
}
|
||||||
|
function ConsoleRow(props: Props) {
|
||||||
|
const { log, iconProps, jump, renderWithNL } = props;
|
||||||
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
const lines = log.value.split('\n').filter((l: any) => !!l);
|
||||||
|
const canExpand = lines.length > 1;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(stl.line, 'flex py-2 px-4 overflow-hidden group relative', {
|
||||||
|
info: !log.isYellow() && !log.isRed(),
|
||||||
|
warn: log.isYellow(),
|
||||||
|
error: log.isRed(),
|
||||||
|
'cursor-pointer': canExpand,
|
||||||
|
})}
|
||||||
|
onClick={() => setExpanded(!expanded)}
|
||||||
|
>
|
||||||
|
<div className={cn(stl.timestamp)}>
|
||||||
|
<Icon size="14" className={stl.icon} {...iconProps} />
|
||||||
|
</div>
|
||||||
|
{/* <div className={cn(stl.timestamp, {})}>
|
||||||
|
{Duration.fromMillis(log.time).toFormat('mm:ss.SSS')}
|
||||||
|
</div> */}
|
||||||
|
<div key={log.key} className={cn('')} data-scroll-item={log.isRed()}>
|
||||||
|
<div className={cn(stl.message, 'flex items-center')}>
|
||||||
|
{canExpand && <Icon name="caret-right-fill" className="mr-2" />}
|
||||||
|
<span>{renderWithNL(lines.pop())}</span>
|
||||||
|
</div>
|
||||||
|
{canExpand && expanded && lines.map((l: any) => <div className="ml-4 mb-1">{l}</div>)}
|
||||||
|
</div>
|
||||||
|
<JumpButton onClick={() => jump(log.time)} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConsoleRow;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './ConsoleRow';
|
||||||
|
|
@ -55,15 +55,15 @@ export default class Exceptions extends React.PureComponent {
|
||||||
|
|
||||||
const filtered = exceptions.filter((e) => filterRE.test(e.name) || filterRE.test(e.message));
|
const filtered = exceptions.filter((e) => filterRE.test(e.name) || filterRE.test(e.message));
|
||||||
|
|
||||||
let lastIndex = -1;
|
// let lastIndex = -1;
|
||||||
filtered.forEach((item, index) => {
|
// filtered.forEach((item, index) => {
|
||||||
if (
|
// if (
|
||||||
this.props.exceptionsNow.length > 0 &&
|
// this.props.exceptionsNow.length > 0 &&
|
||||||
item.time <= this.props.exceptionsNow[this.props.exceptionsNow.length - 1].time
|
// item.time <= this.props.exceptionsNow[this.props.exceptionsNow.length - 1].time
|
||||||
) {
|
// ) {
|
||||||
lastIndex = index;
|
// lastIndex = index;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -133,13 +133,13 @@ export default class Exceptions extends React.PureComponent {
|
||||||
</BottomBlock.Header>
|
</BottomBlock.Header>
|
||||||
<BottomBlock.Content>
|
<BottomBlock.Content>
|
||||||
<NoContent size="small" show={filtered.length === 0} title="No recordings found">
|
<NoContent size="small" show={filtered.length === 0} title="No recordings found">
|
||||||
<Autoscroll autoScrollTo={Math.max(lastIndex, 0)}>
|
<Autoscroll>
|
||||||
{filtered.map((e, index) => (
|
{filtered.map((e, index) => (
|
||||||
<ErrorItem
|
<ErrorItem
|
||||||
onJump={() => jump(e.time)}
|
onJump={() => jump(e.time)}
|
||||||
error={e}
|
error={e}
|
||||||
key={e.key}
|
key={e.key}
|
||||||
selected={lastIndex === index}
|
// selected={lastIndex === index}
|
||||||
// inactive={index > lastIndex}
|
// inactive={index > lastIndex}
|
||||||
onErrorClick={(jsEvent) => {
|
onErrorClick={(jsEvent) => {
|
||||||
jsEvent.stopPropagation();
|
jsEvent.stopPropagation();
|
||||||
|
|
|
||||||
|
|
@ -69,20 +69,20 @@ export default class StackEvents extends React.PureComponent {
|
||||||
({ key }) => key === ALL || stackEvents.some(({ source }) => key === source)
|
({ key }) => key === ALL || stackEvents.some(({ source }) => key === source)
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredStackEvents = stackEvents
|
const filteredStackEvents = stackEvents.filter(
|
||||||
// .filter(({ data }) => data.includes(filter))
|
({ source }) => activeTab === ALL || activeTab === source
|
||||||
.filter(({ source }) => activeTab === ALL || activeTab === source);
|
);
|
||||||
|
|
||||||
let lastIndex = -1;
|
// let lastIndex = -1;
|
||||||
// TODO: Need to do filtering in store, or preferably in a selector
|
// // TODO: Need to do filtering in store, or preferably in a selector
|
||||||
filteredStackEvents.forEach((item, index) => {
|
// filteredStackEvents.forEach((item, index) => {
|
||||||
if (
|
// if (
|
||||||
this.props.stackEventsNow.length > 0 &&
|
// this.props.stackEventsNow.length > 0 &&
|
||||||
item.time <= this.props.stackEventsNow[this.props.stackEventsNow.length - 1].time
|
// item.time <= this.props.stackEventsNow[this.props.stackEventsNow.length - 1].time
|
||||||
) {
|
// ) {
|
||||||
lastIndex = index;
|
// lastIndex = index;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -154,13 +154,13 @@ export default class StackEvents extends React.PureComponent {
|
||||||
size="small"
|
size="small"
|
||||||
show={filteredStackEvents.length === 0}
|
show={filteredStackEvents.length === 0}
|
||||||
>
|
>
|
||||||
<Autoscroll autoScrollTo={Math.max(lastIndex, 0)}>
|
<Autoscroll>
|
||||||
{filteredStackEvents.map((userEvent, index) => (
|
{filteredStackEvents.map((userEvent, index) => (
|
||||||
<UserEvent
|
<UserEvent
|
||||||
key={userEvent.key}
|
key={userEvent.key}
|
||||||
onDetailsClick={this.onDetailsClick.bind(this)}
|
onDetailsClick={this.onDetailsClick.bind(this)}
|
||||||
inactive={index > lastIndex}
|
// inactive={index > lastIndex}
|
||||||
selected={lastIndex === index}
|
// selected={lastIndex === index}
|
||||||
userEvent={userEvent}
|
userEvent={userEvent}
|
||||||
onJump={() => jump(userEvent.time)}
|
onJump={() => jump(userEvent.time)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { OPENREPLAY, SENTRY, DATADOG, STACKDRIVER } from 'Types/session/stackEvent';
|
import { OPENREPLAY, SENTRY, DATADOG, STACKDRIVER } from 'Types/session/stackEvent';
|
||||||
import { Icon, IconButton } from 'UI';
|
import { Icon } from 'UI';
|
||||||
import withToggle from 'HOCs/withToggle';
|
import withToggle from 'HOCs/withToggle';
|
||||||
import Sentry from './Sentry';
|
import Sentry from './Sentry';
|
||||||
import JsonViewer from './JsonViewer';
|
import JsonViewer from './JsonViewer';
|
||||||
import stl from './userEvent.module.css';
|
import stl from './userEvent.module.css';
|
||||||
import { Duration } from 'luxon';
|
import { Duration } from 'luxon';
|
||||||
|
import JumpButton from 'Shared/DevTools/JumpButton';
|
||||||
|
|
||||||
// const modalSources = [ SENTRY, DATADOG ];
|
// const modalSources = [ SENTRY, DATADOG ];
|
||||||
|
|
||||||
|
|
@ -34,34 +35,30 @@ export default class UserEvent extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { userEvent, inactive, selected } = this.props;
|
const { userEvent, inactive, selected } = this.props;
|
||||||
//const message = this.getEventMessage();
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-scroll-item={userEvent.isRed()}
|
data-scroll-item={userEvent.isRed()}
|
||||||
// onClick={ this.props.switchOpen } //
|
onClick={this.onClickDetails}
|
||||||
onClick={this.props.onJump} //
|
className={cn(
|
||||||
className={cn('group flex py-2 px-4 ', stl.userEvent, this.getLevelClassname(), {
|
'group flex items-center py-2 px-4 border-b cursor-pointer',
|
||||||
// [stl.inactive]: inactive,
|
// stl.userEvent,
|
||||||
[stl.selected]: selected,
|
// this.getLevelClassname(),
|
||||||
})}
|
// {
|
||||||
|
// [stl.selected]: selected,
|
||||||
|
// },
|
||||||
|
'hover:bg-active-blue'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<div className={'self-start pr-4'}>
|
{/* <div className={'self-start pr-4'}>
|
||||||
{Duration.fromMillis(userEvent.time).toFormat('mm:ss.SSS')}
|
{Duration.fromMillis(userEvent.time).toFormat('mm:ss.SSS')}
|
||||||
</div>
|
</div> */}
|
||||||
<div className={cn('mr-auto', stl.infoWrapper)}>
|
<div className={cn('mr-auto', stl.infoWrapper)}>
|
||||||
<div className={stl.title}>
|
<div className={stl.title}>
|
||||||
<Icon {...this.getIconProps()} />
|
<Icon {...this.getIconProps()} />
|
||||||
{userEvent.name}
|
<span className="capitalize">{userEvent.name}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="self-center">
|
<JumpButton onClick={this.props.onJump} />
|
||||||
<IconButton
|
|
||||||
outline={!userEvent.isRed()}
|
|
||||||
red={userEvent.isRed()}
|
|
||||||
onClick={this.onClickDetails}
|
|
||||||
label="DETAILS"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Icon, Popup } from 'UI';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onClick: any;
|
||||||
|
tooltip?: string;
|
||||||
|
}
|
||||||
|
function JumpButton(props: Props) {
|
||||||
|
const { tooltip = '' } = props;
|
||||||
|
return (
|
||||||
|
<Popup content={tooltip} disabled={!!tooltip}>
|
||||||
|
<div
|
||||||
|
className="invisible group-hover:visible rounded-lg bg-active-blue text-xs flex items-center px-2 py-1 color-teal absolute right-0 top-0 bottom-0"
|
||||||
|
onClick={(e: any) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
props.onClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name="caret-right-fill" size="12" color="teal" />
|
||||||
|
<span>JUMP</span>
|
||||||
|
</div>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default JumpButton;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './JumpButton';
|
||||||
|
|
@ -9,6 +9,7 @@ import BarRow from './BarRow';
|
||||||
import stl from './timeTable.module.css';
|
import stl from './timeTable.module.css';
|
||||||
|
|
||||||
import autoscrollStl from '../autoscroll.module.css'; //aaa
|
import autoscrollStl from '../autoscroll.module.css'; //aaa
|
||||||
|
import JumpButton from '../JumpButton';
|
||||||
|
|
||||||
type Timed = {
|
type Timed = {
|
||||||
time: number;
|
time: number;
|
||||||
|
|
@ -187,8 +188,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onJump = (e: any, index: any) => {
|
onJump = (index: any) => {
|
||||||
e.stopPropagation();
|
|
||||||
if (this.props.onJump) {
|
if (this.props.onJump) {
|
||||||
this.props.onJump(this.props.rows[index].time);
|
this.props.onJump(this.props.rows[index].time);
|
||||||
}
|
}
|
||||||
|
|
@ -223,13 +223,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
||||||
<div className={cn('relative flex-1 flex', stl.timeBarWrapper)}>
|
<div className={cn('relative flex-1 flex', stl.timeBarWrapper)}>
|
||||||
<BarRow resource={row} timestart={timestart} timewidth={timewidth} popup={renderPopup} />
|
<BarRow resource={row} timestart={timestart} timewidth={timewidth} popup={renderPopup} />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<JumpButton onClick={() => this.onJump(index)} />
|
||||||
className="invisible group-hover:visible rounded-lg bg-active-blue text-xs flex items-center px-2 py-1 color-teal"
|
|
||||||
onClick={(e) => this.onJump(e, index)}
|
|
||||||
>
|
|
||||||
<Icon name="caret-right-fill" size="12" color="teal" />
|
|
||||||
<span>JUMP</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ $offset: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.hoverable {
|
.hoverable {
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
|
|
||||||
|
|
@ -26,16 +26,16 @@ function GraphQLDetailsModal(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-5 bg-white h-screen overflow-y-auto" style={{ width: '500px' }}>
|
<div className="p-5 bg-white h-screen overflow-y-auto" style={{ width: '500px' }}>
|
||||||
<h5 className="mb-2">{'Operation Name'}</h5>
|
<h5 className="mb-2 font-medium">{'Operation Name'}</h5>
|
||||||
<div className={dataClass}>{operationName}</div>
|
<div className={dataClass}>{operationName}</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-4 mt-4">
|
<div className="flex items-center gap-4 mt-4">
|
||||||
<div className="w-6/12">
|
<div className="w-6/12">
|
||||||
<div className="mb-2">Operation Kind</div>
|
<div className="mb-2 font-medium">Operation Kind</div>
|
||||||
<div className={dataClass}>{operationKind}</div>
|
<div className={dataClass}>{operationKind}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-6/12">
|
<div className="w-6/12">
|
||||||
<div className="mb-2">Duration</div>
|
<div className="mb-2 font-medium">Duration</div>
|
||||||
<div className={dataClass}>{duration ? parseInt(duration) : '???'} ms</div>
|
<div className={dataClass}>{duration ? parseInt(duration) : '???'} ms</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -43,7 +43,7 @@ function GraphQLDetailsModal(props: Props) {
|
||||||
<div style={{ height: 'calc(100vh - 314px)', overflowY: 'auto' }}>
|
<div style={{ height: 'calc(100vh - 314px)', overflowY: 'auto' }}>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex justify-between items-start mt-6 mb-2">
|
<div className="flex justify-between items-start mt-6 mb-2">
|
||||||
<h5 className="mt-1 mr-1">{'Variables'}</h5>
|
<h5 className="mt-1 mr-1 font-medium">{'Variables'}</h5>
|
||||||
</div>
|
</div>
|
||||||
<div className={dataClass}>
|
<div className={dataClass}>
|
||||||
{jsonVars === undefined ? variables : <JSONTree src={jsonVars} />}
|
{jsonVars === undefined ? variables : <JSONTree src={jsonVars} />}
|
||||||
|
|
@ -53,7 +53,7 @@ function GraphQLDetailsModal(props: Props) {
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className="flex justify-between items-start mt-6 mb-2">
|
<div className="flex justify-between items-start mt-6 mb-2">
|
||||||
<h5 className="mt-1 mr-1">{'Response'}</h5>
|
<h5 className="mt-1 mr-1 font-medium">{'Response'}</h5>
|
||||||
</div>
|
</div>
|
||||||
<div className={dataClass}>
|
<div className={dataClass}>
|
||||||
{jsonResponse === undefined ? response : <JSONTree src={jsonResponse} />}
|
{jsonResponse === undefined ? response : <JSONTree src={jsonResponse} />}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ function ErrorItem({ error = {}, onJump, inactive, selected }) {
|
||||||
|
|
||||||
const onErrorClick = () => {
|
const onErrorClick = () => {
|
||||||
showModal(<ErrorDetailsModal errorId={error.errorId} />, { right: true });
|
showModal(<ErrorDetailsModal errorId={error.errorId} />, { right: true });
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(stl.wrapper, 'py-2 px-4 flex cursor-pointer', {
|
className={cn(stl.wrapper, 'py-2 px-4 flex cursor-pointer', {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue