change(ui) - events and console improvements
This commit is contained in:
parent
c11e3b9069
commit
c47d06ae43
20 changed files with 335 additions and 47 deletions
|
|
@ -17,7 +17,7 @@ function ConsoleRow(props: Props) {
|
|||
const canExpand = lines.length > 1;
|
||||
return (
|
||||
<div
|
||||
className={cn(stl.line, 'flex py-2 px-4 overflow-hidden group relative', {
|
||||
className={cn(stl.line, 'flex py-2 px-4 overflow-hidden group relative select-none', {
|
||||
info: !log.isYellow() && !log.isRed(),
|
||||
warn: log.isYellow(),
|
||||
error: log.isRed(),
|
||||
|
|
@ -33,7 +33,9 @@ function ConsoleRow(props: Props) {
|
|||
</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" />}
|
||||
{canExpand && (
|
||||
<Icon name={expanded ? 'caret-down-fill' : '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>)}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import BottomBlock from '../BottomBlock';
|
|||
@connectPlayer((state) => ({
|
||||
logs: state.logListNow,
|
||||
exceptions: state.exceptionsList,
|
||||
exceptionsNow: state.exceptionsListNow,
|
||||
// exceptionsNow: state.exceptionsListNow,
|
||||
}))
|
||||
@connect(
|
||||
(state) => ({
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const ControlButton = ({
|
|||
icon = '',
|
||||
disabled = false,
|
||||
onClick,
|
||||
count = 0,
|
||||
// count = 0,
|
||||
hasErrors = false,
|
||||
active = false,
|
||||
size = 20,
|
||||
|
|
@ -31,7 +31,7 @@ const ControlButton = ({
|
|||
>
|
||||
<div className={stl.labels}>
|
||||
{hasErrors && <div className={stl.errorSymbol} />}
|
||||
{count > 0 && <div className={stl.countLabel}>{count}</div>}
|
||||
{/* {count > 0 && <div className={stl.countLabel}>{count}</div>} */}
|
||||
</div>
|
||||
{!noIcon && <Icon name={icon} size={size} color="gray-dark" />}
|
||||
{!noLabel && (
|
||||
|
|
|
|||
|
|
@ -95,23 +95,23 @@ function getStorageName(type) {
|
|||
disabled: state.cssLoading || state.messagesLoading || state.inspectorMode || state.markedTargets,
|
||||
inspectorMode: state.inspectorMode,
|
||||
fullscreenDisabled: state.messagesLoading,
|
||||
logCount: state.logListNow.length,
|
||||
logRedCount: state.logRedCountNow,
|
||||
resourceRedCount: state.resourceRedCountNow,
|
||||
fetchRedCount: state.fetchRedCountNow,
|
||||
logCount: state.logList.length,
|
||||
logRedCount: state.logRedCount,
|
||||
resourceRedCount: state.resourceRedCount,
|
||||
fetchRedCount: state.fetchRedCount,
|
||||
showStack: state.stackList.length > 0,
|
||||
stackCount: state.stackListNow.length,
|
||||
stackRedCount: state.stackRedCountNow,
|
||||
profilesCount: state.profilesListNow.length,
|
||||
stackCount: state.stackList.length,
|
||||
stackRedCount: state.stackRedCount,
|
||||
profilesCount: state.profilesList.length,
|
||||
storageCount: selectStorageListNow(state).length,
|
||||
storageType: selectStorageType(state),
|
||||
showStorage: selectStorageType(state) !== STORAGE_TYPES.NONE,
|
||||
showProfiler: state.profilesList.length > 0,
|
||||
showGraphql: state.graphqlList.length > 0,
|
||||
showFetch: state.fetchCount > 0,
|
||||
fetchCount: state.fetchCountNow,
|
||||
graphqlCount: state.graphqlListNow.length,
|
||||
exceptionsCount: state.exceptionsListNow.length,
|
||||
fetchCount: state.fetchCount,
|
||||
graphqlCount: state.graphqlList.length,
|
||||
exceptionsCount: state.exceptionsList.length,
|
||||
showExceptions: state.exceptionsList.length > 0,
|
||||
showLongtasks: state.longtasksList.length > 0,
|
||||
liveTimeTravel: state.liveTimeTravel,
|
||||
|
|
@ -380,7 +380,7 @@ export default class Controls extends React.Component {
|
|||
label="CONSOLE"
|
||||
noIcon
|
||||
labelClassName="!text-base font-semibold"
|
||||
count={logCount}
|
||||
// count={logCount}
|
||||
hasErrors={logRedCount > 0}
|
||||
containerClassName="mx-2"
|
||||
/>
|
||||
|
|
@ -412,7 +412,7 @@ export default class Controls extends React.Component {
|
|||
disabled={disabled && !inspectorMode}
|
||||
onClick={() => toggleBottomTools(GRAPHQL)}
|
||||
active={bottomBlock === GRAPHQL && !inspectorMode}
|
||||
count={graphqlCount}
|
||||
// count={graphqlCount}
|
||||
label="GRAPHQL"
|
||||
noIcon
|
||||
labelClassName="!text-base font-semibold"
|
||||
|
|
@ -424,7 +424,7 @@ export default class Controls extends React.Component {
|
|||
disabled={disabled && !inspectorMode}
|
||||
onClick={() => toggleBottomTools(STORAGE)}
|
||||
active={bottomBlock === STORAGE && !inspectorMode}
|
||||
count={storageCount}
|
||||
// count={storageCount}
|
||||
label={getStorageName(storageType)}
|
||||
noIcon
|
||||
labelClassName="!text-base font-semibold"
|
||||
|
|
@ -440,7 +440,7 @@ export default class Controls extends React.Component {
|
|||
noIcon
|
||||
labelClassName="!text-base font-semibold"
|
||||
containerClassName="mx-2"
|
||||
count={exceptionsCount}
|
||||
// count={exceptionsCount}
|
||||
hasErrors={exceptionsCount > 0}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -453,7 +453,7 @@ export default class Controls extends React.Component {
|
|||
noIcon
|
||||
labelClassName="!text-base font-semibold"
|
||||
containerClassName="mx-2"
|
||||
count={stackCount}
|
||||
// count={stackCount}
|
||||
hasErrors={stackRedCount > 0}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -462,7 +462,7 @@ export default class Controls extends React.Component {
|
|||
disabled={disabled && !inspectorMode}
|
||||
onClick={() => toggleBottomTools(PROFILER)}
|
||||
active={bottomBlock === PROFILER && !inspectorMode}
|
||||
count={profilesCount}
|
||||
// count={profilesCount}
|
||||
label="PROFILER"
|
||||
noIcon
|
||||
labelClassName="!text-base font-semibold"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
INSPECTOR,
|
||||
OVERVIEW,
|
||||
} from 'Duck/components/player';
|
||||
import NetworkPanel from 'Shared/DevTools/NetworkPanel/NetworkPanel';
|
||||
import NetworkPanel from 'Shared/DevTools/NetworkPanel';
|
||||
import Console from '../Console/Console';
|
||||
import StackEvents from '../StackEvents/StackEvents';
|
||||
import Storage from '../Storage';
|
||||
|
|
@ -40,6 +40,7 @@ import Overlay from './Overlay';
|
|||
import stl from './player.module.css';
|
||||
import { updateLastPlayedSession } from 'Duck/sessions';
|
||||
import OverviewPanel from '../OverviewPanel';
|
||||
import ConsolePanel from 'Shared/DevTools/ConsolePanel';
|
||||
|
||||
@connectPlayer((state) => ({
|
||||
live: state.live,
|
||||
|
|
@ -108,7 +109,7 @@ export default class Player extends React.PureComponent {
|
|||
{!fullscreen && !!bottomBlock && (
|
||||
<div style={{ maxWidth, width: '100%' }}>
|
||||
{bottomBlock === OVERVIEW && <OverviewPanel />}
|
||||
{bottomBlock === CONSOLE && <Console />}
|
||||
{bottomBlock === CONSOLE && <ConsolePanel />}
|
||||
{bottomBlock === NETWORK && (
|
||||
// <Network />
|
||||
<NetworkPanel />
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import withEnumToggle from 'HOCs/withEnumToggle';
|
|||
import { connectPlayer, jump } from 'Player';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import StackEventRow from 'Shared/DevTools/StackEventRow';
|
||||
import { DATADOG, SENTRY, STACKDRIVER, typeList } from 'Types/session/stackEvent';
|
||||
import { NoContent, SlideModal, Tabs, Link } from 'UI';
|
||||
import Autoscroll from '../Autoscroll';
|
||||
|
|
@ -19,7 +20,7 @@ const TABS = [ALL, ...typeList].map((tab) => ({ text: tab, key: tab }));
|
|||
@withEnumToggle('activeTab', 'setActiveTab', ALL)
|
||||
@connectPlayer((state) => ({
|
||||
stackEvents: state.stackList,
|
||||
stackEventsNow: state.stackListNow,
|
||||
// stackEventsNow: state.stackListNow,
|
||||
}))
|
||||
@connect(
|
||||
(state) => ({
|
||||
|
|
@ -156,14 +157,19 @@ export default class StackEvents extends React.PureComponent {
|
|||
>
|
||||
<Autoscroll>
|
||||
{filteredStackEvents.map((userEvent, index) => (
|
||||
<UserEvent
|
||||
<StackEventRow
|
||||
key={userEvent.key}
|
||||
onDetailsClick={this.onDetailsClick.bind(this)}
|
||||
// inactive={index > lastIndex}
|
||||
// selected={lastIndex === index}
|
||||
userEvent={userEvent}
|
||||
event={userEvent}
|
||||
onJump={() => jump(userEvent.time)}
|
||||
/>
|
||||
// <UserEvent
|
||||
// key={userEvent.key}
|
||||
// onDetailsClick={this.onDetailsClick.bind(this)}
|
||||
// // inactive={index > lastIndex}
|
||||
// // selected={lastIndex === index}
|
||||
// userEvent={userEvent}
|
||||
// onJump={() => jump(userEvent.time)}
|
||||
// />
|
||||
))}
|
||||
</Autoscroll>
|
||||
</NoContent>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ export default class JsonViewer extends React.PureComponent {
|
|||
{isObjectData && <JSONTree src={data} collapsed={false} />}
|
||||
{!isObjectData && Array.isArray(data) && (
|
||||
<div>
|
||||
<div className="text-lg">{data[0]}</div>
|
||||
<div className="code-font mb-2">
|
||||
{typeof data[0] === 'string' ? data[0] : JSON.stringify(data[0])}
|
||||
</div>
|
||||
<JSONTree src={data[1]} collapsed={false} />
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -35,12 +35,14 @@ export default class UserEvent extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const { userEvent, inactive, selected } = this.props;
|
||||
let message = userEvent.payload[0] || '';
|
||||
message = typeof message === 'string' ? message : JSON.stringify(message);
|
||||
return (
|
||||
<div
|
||||
data-scroll-item={userEvent.isRed()}
|
||||
onClick={this.onClickDetails}
|
||||
className={cn(
|
||||
'group flex items-center py-2 px-4 border-b cursor-pointer',
|
||||
'group flex items-center py-2 px-4 border-b cursor-pointer relative',
|
||||
// stl.userEvent,
|
||||
// this.getLevelClassname(),
|
||||
// {
|
||||
|
|
@ -52,10 +54,11 @@ export default class UserEvent extends React.PureComponent {
|
|||
{/* <div className={'self-start pr-4'}>
|
||||
{Duration.fromMillis(userEvent.time).toFormat('mm:ss.SSS')}
|
||||
</div> */}
|
||||
<div className={cn('mr-auto', stl.infoWrapper)}>
|
||||
<div className={stl.title}>
|
||||
<Icon {...this.getIconProps()} />
|
||||
<span className="capitalize">{userEvent.name}</span>
|
||||
<div className={cn('mr-auto flex items-start')}>
|
||||
<Icon {...this.getIconProps()} />
|
||||
<div>
|
||||
<div className="capitalize font-medium mb-1">{userEvent.name}</div>
|
||||
<div className="code-font text-xs">{message}</div>
|
||||
</div>
|
||||
</div>
|
||||
<JumpButton onClick={this.props.onJump} />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
import React, { useState } from 'react';
|
||||
import { connectPlayer, jump } from 'Player';
|
||||
// import Log from 'Types/session/log';
|
||||
import BottomBlock from '../BottomBlock';
|
||||
import { LEVEL } from 'Types/session/log';
|
||||
import { Tabs, Input, Icon, NoContent } from 'UI';
|
||||
// import Autoscroll from 'App/components/Session_/Autoscroll';
|
||||
import cn from 'classnames';
|
||||
import ConsoleRow from '../ConsoleRow';
|
||||
import { getRE } from 'App/utils';
|
||||
|
||||
const ALL = 'ALL';
|
||||
const INFO = 'INFO';
|
||||
const WARNINGS = 'WARNINGS';
|
||||
const ERRORS = 'ERRORS';
|
||||
|
||||
const LEVEL_TAB = {
|
||||
[LEVEL.INFO]: INFO,
|
||||
[LEVEL.LOG]: INFO,
|
||||
[LEVEL.WARNING]: WARNINGS,
|
||||
[LEVEL.ERROR]: ERRORS,
|
||||
[LEVEL.EXCEPTION]: ERRORS,
|
||||
};
|
||||
|
||||
const TABS = [ALL, ERRORS, WARNINGS, INFO].map((tab) => ({ text: tab, key: tab }));
|
||||
|
||||
function renderWithNL(s = '') {
|
||||
if (typeof s !== 'string') return '';
|
||||
return s.split('\n').map((line, i) => <div className={cn({ 'ml-20': i !== 0 })}>{line}</div>);
|
||||
}
|
||||
|
||||
const getIconProps = (level: any) => {
|
||||
switch (level) {
|
||||
case LEVEL.INFO:
|
||||
case LEVEL.LOG:
|
||||
return {
|
||||
name: 'console/info',
|
||||
color: 'blue2',
|
||||
};
|
||||
case LEVEL.WARN:
|
||||
case LEVEL.WARNING:
|
||||
return {
|
||||
name: 'console/warning',
|
||||
color: 'red2',
|
||||
};
|
||||
case LEVEL.ERROR:
|
||||
return {
|
||||
name: 'console/error',
|
||||
color: 'red',
|
||||
};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
interface Props {
|
||||
logs: any;
|
||||
exceptions: any;
|
||||
}
|
||||
function ConsolePanel(props: Props) {
|
||||
const { logs } = props;
|
||||
const additionalHeight = 0;
|
||||
const [activeTab, setActiveTab] = useState(ALL);
|
||||
const [filter, setFilter] = useState('');
|
||||
|
||||
let filtered = React.useMemo(() => {
|
||||
const filterRE = getRE(filter, 'i');
|
||||
let list = logs;
|
||||
|
||||
list = list.filter(
|
||||
({ value, level }: any) =>
|
||||
(!!filter ? filterRE.test(value) : true) &&
|
||||
(activeTab === ALL || activeTab === LEVEL_TAB[level])
|
||||
);
|
||||
return list;
|
||||
}, [filter, activeTab]);
|
||||
|
||||
const onTabClick = (activeTab: any) => setActiveTab(activeTab);
|
||||
const onFilterChange = ({ target: { value } }: any) => setFilter(value);
|
||||
|
||||
return (
|
||||
<BottomBlock style={{ height: 300 + additionalHeight + 'px' }}>
|
||||
<BottomBlock.Header>
|
||||
<div className="flex items-center">
|
||||
<span className="font-semibold color-gray-medium mr-4">Console</span>
|
||||
<Tabs tabs={TABS} active={activeTab} onClick={onTabClick} border={false} />
|
||||
</div>
|
||||
<Input
|
||||
className="input-small h-8"
|
||||
placeholder="Filter by keyword"
|
||||
icon="search"
|
||||
iconPosition="left"
|
||||
name="filter"
|
||||
height={28}
|
||||
onChange={onFilterChange}
|
||||
/>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content className="overflow-y-auto">
|
||||
<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> */}
|
||||
{filtered.map((l: any, index: any) => (
|
||||
<ConsoleRow
|
||||
key={index}
|
||||
log={l}
|
||||
jump={jump}
|
||||
iconProps={getIconProps(l.level)}
|
||||
renderWithNL={renderWithNL}
|
||||
/>
|
||||
))}
|
||||
{/* </Autoscroll> */}
|
||||
</NoContent>
|
||||
</BottomBlock.Content>
|
||||
</BottomBlock>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectPlayer((state: any) => {
|
||||
const logs = state.logList;
|
||||
// const exceptions = state.exceptionsList; // TODO merge
|
||||
return {
|
||||
logs,
|
||||
};
|
||||
})(ConsolePanel);
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './ConsolePanel';
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
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('border-b flex items-center py-2 px-4 overflow-hidden group relative select-none', {
|
||||
info: !log.isYellow() && !log.isRed(),
|
||||
warn: log.isYellow(),
|
||||
error: log.isRed(),
|
||||
'cursor-pointer': canExpand,
|
||||
})}
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
>
|
||||
<div className="mr-2">
|
||||
<Icon size="14" {...iconProps} />
|
||||
</div>
|
||||
<div key={log.key} data-scroll-item={log.isRed()}>
|
||||
<div className={cn('flex items-center')}>
|
||||
{canExpand && (
|
||||
<Icon name={expanded ? 'caret-down-fill' : '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';
|
||||
|
|
@ -10,7 +10,7 @@ function JumpButton(props: 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"
|
||||
className="mr-2 border cursor-pointer 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 hover:shadow h-6 my-auto"
|
||||
onClick={(e: any) => {
|
||||
e.stopPropagation();
|
||||
props.onClick();
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './NetworkPanel'
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import React from 'react';
|
||||
import { DATADOG, SENTRY, STACKDRIVER, typeList } from 'Types/session/stackEvent';
|
||||
import JsonViewer from 'Components/Session_/StackEvents/UserEvent/JsonViewer';
|
||||
import Sentry from 'Components/Session_/StackEvents/UserEvent/Sentry';
|
||||
|
||||
interface Props {
|
||||
event: any;
|
||||
}
|
||||
function StackEventModal(props: Props) {
|
||||
const { event } = props;
|
||||
const renderPopupContent = () => {
|
||||
const { source, payload, name } = event;
|
||||
switch (source) {
|
||||
case SENTRY:
|
||||
return <Sentry event={payload} />;
|
||||
case DATADOG:
|
||||
return <JsonViewer title={name} data={payload} icon="integrations/datadog" />;
|
||||
case STACKDRIVER:
|
||||
return <JsonViewer title={name} data={payload} icon="integrations/stackdriver" />;
|
||||
default:
|
||||
return <JsonViewer title={name} data={payload} icon={`integrations/${source}`} />;
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="bg-white overflow-y-auto h-screen p-5" style={{ width: '500px' }}>
|
||||
<h5 className="mb-2 text-2xl">Stack Event</h5>
|
||||
{renderPopupContent()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default StackEventModal;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './StackEventModal';
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import React from 'react';
|
||||
import JumpButton from '../JumpButton';
|
||||
import { Icon } from 'UI';
|
||||
import cn from 'classnames';
|
||||
import { OPENREPLAY, SENTRY, DATADOG, STACKDRIVER } from 'Types/session/stackEvent';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import StackEventModal from '../StackEventModal';
|
||||
|
||||
interface Props {
|
||||
event: any;
|
||||
onJump: any;
|
||||
}
|
||||
function StackEventRow(props: Props) {
|
||||
const { event, onJump } = props;
|
||||
let message = event.payload[0] || '';
|
||||
message = typeof message === 'string' ? message : JSON.stringify(message);
|
||||
const onClickDetails = () => {
|
||||
showModal(<StackEventModal event={event} />, { right: true });
|
||||
};
|
||||
const { showModal } = useModal();
|
||||
|
||||
const iconProps: any = React.useMemo(() => {
|
||||
const { source } = event;
|
||||
return {
|
||||
name: `integrations/${source}`,
|
||||
size: 18,
|
||||
marginRight: source === OPENREPLAY ? 11 : 10,
|
||||
};
|
||||
}, [event]);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-scroll-item={event.isRed()}
|
||||
onClick={onClickDetails}
|
||||
className={cn(
|
||||
'group flex items-center py-2 px-4 border-b cursor-pointer relative',
|
||||
'hover:bg-active-blue'
|
||||
)}
|
||||
>
|
||||
<div className={cn('mr-auto flex items-start')}>
|
||||
<Icon {...iconProps} />
|
||||
<div>
|
||||
<div className="capitalize font-medium mb-1">{event.name}</div>
|
||||
<div className="code-font text-xs">{message}</div>
|
||||
</div>
|
||||
</div>
|
||||
<JumpButton onClick={onJump} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default StackEventRow;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './StackEventRow';
|
||||
|
|
@ -5,8 +5,15 @@ import stl from './errorItem.module.css';
|
|||
import { Duration } from 'luxon';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import ErrorDetailsModal from 'App/components/Dashboard/components/Errors/ErrorDetailsModal';
|
||||
import JumpButton from 'Shared/DevTools/JumpButton';
|
||||
|
||||
function ErrorItem({ error = {}, onJump, inactive, selected }) {
|
||||
interface Props {
|
||||
error: any;
|
||||
onJump: any;
|
||||
inactive?: Boolean;
|
||||
selected?: Boolean;
|
||||
}
|
||||
function ErrorItem({ error = {}, onJump, inactive, selected }: Props) {
|
||||
const { showModal } = useModal();
|
||||
|
||||
const onErrorClick = () => {
|
||||
|
|
@ -14,25 +21,27 @@ function ErrorItem({ error = {}, onJump, inactive, selected }) {
|
|||
};
|
||||
return (
|
||||
<div
|
||||
className={cn(stl.wrapper, 'py-2 px-4 flex cursor-pointer', {
|
||||
[stl.inactive]: inactive,
|
||||
[stl.selected]: selected,
|
||||
className={cn(stl.wrapper, 'py-2 px-4 flex cursor-pointer hover:bg-active-blue relative group', {
|
||||
// [stl.inactive]: inactive,
|
||||
// [stl.selected]: selected,
|
||||
})}
|
||||
onClick={onJump}
|
||||
onClick={onErrorClick}
|
||||
// onClick={onJump}
|
||||
>
|
||||
<div className={'self-start pr-4 color-red'}>
|
||||
{/* <div className={'self-start pr-4 color-red'}>
|
||||
{Duration.fromMillis(error.time).toFormat('mm:ss.SSS')}
|
||||
</div>
|
||||
<div className="mr-auto overflow-hidden">
|
||||
</div> */}
|
||||
<div className="overflow-hidden">
|
||||
<div className="color-red mb-1 cursor-pointer code-font">
|
||||
{error.name}
|
||||
<span className="color-gray-darkest ml-2">{error.stack0InfoString}</span>
|
||||
</div>
|
||||
<div className="text-sm color-gray-medium">{error.message}</div>
|
||||
<div className="text-xs code-font">{error.message}</div>
|
||||
</div>
|
||||
<div className="self-center">
|
||||
{/* <div className="self-center">
|
||||
<IconButton red onClick={onErrorClick} label="DETAILS" />
|
||||
</div>
|
||||
</div> */}
|
||||
<JumpButton onClick={onJump} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue