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 BottomBlock from '../BottomBlock';
|
||||
import stl from './console.module.css';
|
||||
import { Duration } from 'luxon';
|
||||
import ConsoleRow from './ConsoleRow';
|
||||
// import { Duration } from 'luxon';
|
||||
|
||||
const ALL = 'ALL';
|
||||
const INFO = 'INFO';
|
||||
|
|
@ -93,35 +94,24 @@ export default class ConsoleContent extends React.PureComponent {
|
|||
/>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content>
|
||||
<NoContent title={
|
||||
<NoContent
|
||||
title={
|
||||
<div className="capitalize flex items-center mt-16">
|
||||
<Icon name="info-circle" className="mr-2" size="18" />
|
||||
No Data
|
||||
</div>
|
||||
} size="small" show={filtered.length === 0}>
|
||||
<Autoscroll autoScrollTo={Math.max(lastIndex, 0)}>
|
||||
{filtered.map((l, index) => (
|
||||
<div
|
||||
className={cn(stl.line, 'flex py-2 px-4', {
|
||||
info: !l.isYellow() && !l.isRed(),
|
||||
warn: l.isYellow(),
|
||||
error: l.isRed(),
|
||||
[stl.activeRow]: lastIndex === index,
|
||||
// [stl.inactiveRow]: index > lastIndex,
|
||||
'cursor-pointer': !isResult,
|
||||
})}
|
||||
onClick={() => !isResult && jump(l.time)}
|
||||
}
|
||||
size="small"
|
||||
show={filtered.length === 0}
|
||||
>
|
||||
<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 autoScrollTo={Math.max(lastIndex, 0)}>
|
||||
{filtered.map((l) => (
|
||||
<ConsoleRow
|
||||
log={l}
|
||||
jump={jump}
|
||||
iconProps={getIconProps(l.level)}
|
||||
renderWithNL={renderWithNL}
|
||||
/>
|
||||
))}
|
||||
</Autoscroll>
|
||||
</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));
|
||||
|
||||
let lastIndex = -1;
|
||||
filtered.forEach((item, index) => {
|
||||
if (
|
||||
this.props.exceptionsNow.length > 0 &&
|
||||
item.time <= this.props.exceptionsNow[this.props.exceptionsNow.length - 1].time
|
||||
) {
|
||||
lastIndex = index;
|
||||
}
|
||||
});
|
||||
// let lastIndex = -1;
|
||||
// filtered.forEach((item, index) => {
|
||||
// if (
|
||||
// this.props.exceptionsNow.length > 0 &&
|
||||
// item.time <= this.props.exceptionsNow[this.props.exceptionsNow.length - 1].time
|
||||
// ) {
|
||||
// lastIndex = index;
|
||||
// }
|
||||
// });
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -133,13 +133,13 @@ export default class Exceptions extends React.PureComponent {
|
|||
</BottomBlock.Header>
|
||||
<BottomBlock.Content>
|
||||
<NoContent size="small" show={filtered.length === 0} title="No recordings found">
|
||||
<Autoscroll autoScrollTo={Math.max(lastIndex, 0)}>
|
||||
<Autoscroll>
|
||||
{filtered.map((e, index) => (
|
||||
<ErrorItem
|
||||
onJump={() => jump(e.time)}
|
||||
error={e}
|
||||
key={e.key}
|
||||
selected={lastIndex === index}
|
||||
// selected={lastIndex === index}
|
||||
// inactive={index > lastIndex}
|
||||
onErrorClick={(jsEvent) => {
|
||||
jsEvent.stopPropagation();
|
||||
|
|
|
|||
|
|
@ -69,20 +69,20 @@ export default class StackEvents extends React.PureComponent {
|
|||
({ key }) => key === ALL || stackEvents.some(({ source }) => key === source)
|
||||
);
|
||||
|
||||
const filteredStackEvents = stackEvents
|
||||
// .filter(({ data }) => data.includes(filter))
|
||||
.filter(({ source }) => activeTab === ALL || activeTab === source);
|
||||
const filteredStackEvents = stackEvents.filter(
|
||||
({ source }) => activeTab === ALL || activeTab === source
|
||||
);
|
||||
|
||||
let lastIndex = -1;
|
||||
// TODO: Need to do filtering in store, or preferably in a selector
|
||||
filteredStackEvents.forEach((item, index) => {
|
||||
if (
|
||||
this.props.stackEventsNow.length > 0 &&
|
||||
item.time <= this.props.stackEventsNow[this.props.stackEventsNow.length - 1].time
|
||||
) {
|
||||
lastIndex = index;
|
||||
}
|
||||
});
|
||||
// let lastIndex = -1;
|
||||
// // TODO: Need to do filtering in store, or preferably in a selector
|
||||
// filteredStackEvents.forEach((item, index) => {
|
||||
// if (
|
||||
// this.props.stackEventsNow.length > 0 &&
|
||||
// item.time <= this.props.stackEventsNow[this.props.stackEventsNow.length - 1].time
|
||||
// ) {
|
||||
// lastIndex = index;
|
||||
// }
|
||||
// });
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -154,13 +154,13 @@ export default class StackEvents extends React.PureComponent {
|
|||
size="small"
|
||||
show={filteredStackEvents.length === 0}
|
||||
>
|
||||
<Autoscroll autoScrollTo={Math.max(lastIndex, 0)}>
|
||||
<Autoscroll>
|
||||
{filteredStackEvents.map((userEvent, index) => (
|
||||
<UserEvent
|
||||
key={userEvent.key}
|
||||
onDetailsClick={this.onDetailsClick.bind(this)}
|
||||
inactive={index > lastIndex}
|
||||
selected={lastIndex === index}
|
||||
// inactive={index > lastIndex}
|
||||
// selected={lastIndex === index}
|
||||
userEvent={userEvent}
|
||||
onJump={() => jump(userEvent.time)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import { OPENREPLAY, SENTRY, DATADOG, STACKDRIVER } from 'Types/session/stackEvent';
|
||||
import { Icon, IconButton } from 'UI';
|
||||
import { Icon } from 'UI';
|
||||
import withToggle from 'HOCs/withToggle';
|
||||
import Sentry from './Sentry';
|
||||
import JsonViewer from './JsonViewer';
|
||||
import stl from './userEvent.module.css';
|
||||
import { Duration } from 'luxon';
|
||||
import JumpButton from 'Shared/DevTools/JumpButton';
|
||||
|
||||
// const modalSources = [ SENTRY, DATADOG ];
|
||||
|
||||
|
|
@ -34,34 +35,30 @@ export default class UserEvent extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const { userEvent, inactive, selected } = this.props;
|
||||
//const message = this.getEventMessage();
|
||||
return (
|
||||
<div
|
||||
data-scroll-item={userEvent.isRed()}
|
||||
// onClick={ this.props.switchOpen } //
|
||||
onClick={this.props.onJump} //
|
||||
className={cn('group flex py-2 px-4 ', stl.userEvent, this.getLevelClassname(), {
|
||||
// [stl.inactive]: inactive,
|
||||
[stl.selected]: selected,
|
||||
})}
|
||||
onClick={this.onClickDetails}
|
||||
className={cn(
|
||||
'group flex items-center py-2 px-4 border-b cursor-pointer',
|
||||
// stl.userEvent,
|
||||
// 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')}
|
||||
</div>
|
||||
</div> */}
|
||||
<div className={cn('mr-auto', stl.infoWrapper)}>
|
||||
<div className={stl.title}>
|
||||
<Icon {...this.getIconProps()} />
|
||||
{userEvent.name}
|
||||
<span className="capitalize">{userEvent.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="self-center">
|
||||
<IconButton
|
||||
outline={!userEvent.isRed()}
|
||||
red={userEvent.isRed()}
|
||||
onClick={this.onClickDetails}
|
||||
label="DETAILS"
|
||||
/>
|
||||
</div>
|
||||
<JumpButton onClick={this.props.onJump} />
|
||||
</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 autoscrollStl from '../autoscroll.module.css'; //aaa
|
||||
import JumpButton from '../JumpButton';
|
||||
|
||||
type Timed = {
|
||||
time: number;
|
||||
|
|
@ -187,8 +188,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
|||
}
|
||||
};
|
||||
|
||||
onJump = (e: any, index: any) => {
|
||||
e.stopPropagation();
|
||||
onJump = (index: any) => {
|
||||
if (this.props.onJump) {
|
||||
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)}>
|
||||
<BarRow resource={row} timestart={timestart} timewidth={timewidth} popup={renderPopup} />
|
||||
</div>
|
||||
<div
|
||||
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>
|
||||
<JumpButton onClick={() => this.onJump(index)} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ $offset: 10px;
|
|||
align-items: center;
|
||||
overflow: hidden;
|
||||
padding: 0 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.hoverable {
|
||||
transition: all 0.3s;
|
||||
|
|
|
|||
|
|
@ -26,16 +26,16 @@ function GraphQLDetailsModal(props: Props) {
|
|||
|
||||
return (
|
||||
<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="flex items-center gap-4 mt-4">
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
|
|
@ -43,7 +43,7 @@ function GraphQLDetailsModal(props: Props) {
|
|||
<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>
|
||||
<h5 className="mt-1 mr-1 font-medium">{'Variables'}</h5>
|
||||
</div>
|
||||
<div className={dataClass}>
|
||||
{jsonVars === undefined ? variables : <JSONTree src={jsonVars} />}
|
||||
|
|
@ -53,7 +53,7 @@ function GraphQLDetailsModal(props: Props) {
|
|||
|
||||
<div>
|
||||
<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 className={dataClass}>
|
||||
{jsonResponse === undefined ? response : <JSONTree src={jsonResponse} />}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ function ErrorItem({ error = {}, onJump, inactive, selected }) {
|
|||
|
||||
const onErrorClick = () => {
|
||||
showModal(<ErrorDetailsModal errorId={error.errorId} />, { right: true });
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={cn(stl.wrapper, 'py-2 px-4 flex cursor-pointer', {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue