change(ui) - console panel changes and other improvements

This commit is contained in:
Shekar Siri 2022-10-11 19:02:51 +02:00
parent a9cf0cfdca
commit c11e3b9069
12 changed files with 144 additions and 88 deletions

View file

@ -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>

View file

@ -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;

View file

@ -0,0 +1 @@
export { default } from './ConsoleRow';

View file

@ -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();

View file

@ -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)}
/> />

View file

@ -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>
); );
} }

View file

@ -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;

View file

@ -0,0 +1 @@
export { default } from './JumpButton';

View file

@ -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>
); );
}; };

View file

@ -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;

View file

@ -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} />}

View file

@ -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', {