ui: group xray events together (#2654)
This commit is contained in:
parent
d0df66b539
commit
4a97094c0e
6 changed files with 456 additions and 140 deletions
|
|
@ -319,10 +319,11 @@ function PanelComponent({
|
|||
isGraph={feature === 'PERFORMANCE'}
|
||||
title={feature}
|
||||
list={resources[feature]}
|
||||
renderElement={(pointer: any) => (
|
||||
renderElement={(pointer: any[], isGrouped: boolean) => (
|
||||
<TimelinePointer
|
||||
pointer={pointer}
|
||||
type={feature}
|
||||
isGrouped={isGrouped}
|
||||
fetchPresented={fetchPresented}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ interface Props {
|
|||
message?: string;
|
||||
className?: string;
|
||||
endTime?: number;
|
||||
renderElement?: (item: any) => React.ReactNode;
|
||||
renderElement?: (item: any, isGrouped: boolean) => React.ReactNode;
|
||||
isGraph?: boolean;
|
||||
zIndex?: number;
|
||||
noMargin?: boolean;
|
||||
|
|
@ -18,15 +18,70 @@ const EventRow = React.memo((props: Props) => {
|
|||
const { title, className, list = [], endTime = 0, isGraph = false, message = '' } = props;
|
||||
const scale = 100 / endTime;
|
||||
const _list =
|
||||
!isGraph &&
|
||||
isGraph ? [] :
|
||||
React.useMemo(() => {
|
||||
return list.map((item: any, _index: number) => {
|
||||
const spread = item.toJS ? { ...item.toJS() } : { ...item };
|
||||
return {
|
||||
...spread,
|
||||
left: getTimelinePosition(item.time, scale),
|
||||
};
|
||||
});
|
||||
const tolerance = 2; // within what %s to group items
|
||||
const groupedItems = [];
|
||||
let currentGroup = [];
|
||||
let currentLeft = 0;
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const item = list[i];
|
||||
const spread = item.toJS ? { ...item.toJS() } : item;
|
||||
const left: number = getTimelinePosition(item.time, scale);
|
||||
const itemWithLeft = { ...spread, left };
|
||||
|
||||
if (currentGroup.length === 0) {
|
||||
currentGroup.push(itemWithLeft);
|
||||
currentLeft = left;
|
||||
} else {
|
||||
if (Math.abs(left - currentLeft) <= tolerance) {
|
||||
currentGroup.push(itemWithLeft);
|
||||
} else {
|
||||
if (currentGroup.length > 1) {
|
||||
const leftValues = currentGroup.map(item => item.left);
|
||||
const minLeft = Math.min(...leftValues);
|
||||
const maxLeft = Math.max(...leftValues);
|
||||
const middleLeft = (minLeft + maxLeft) / 2;
|
||||
|
||||
groupedItems.push({
|
||||
isGrouped: true,
|
||||
items: currentGroup,
|
||||
left: middleLeft,
|
||||
});
|
||||
} else {
|
||||
groupedItems.push({
|
||||
isGrouped: false,
|
||||
items: [currentGroup[0]],
|
||||
left: currentGroup[0].left,
|
||||
});
|
||||
}
|
||||
currentGroup = [itemWithLeft];
|
||||
currentLeft = left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentGroup.length > 1) {
|
||||
const leftValues = currentGroup.map(item => item.left);
|
||||
const minLeft = Math.min(...leftValues);
|
||||
const maxLeft = Math.max(...leftValues);
|
||||
const middleLeft = (minLeft + maxLeft) / 2;
|
||||
|
||||
groupedItems.push({
|
||||
isGrouped: true,
|
||||
items: currentGroup,
|
||||
left: middleLeft,
|
||||
});
|
||||
} else if (currentGroup.length === 1) {
|
||||
groupedItems.push({
|
||||
isGrouped: false,
|
||||
items: [currentGroup[0]],
|
||||
left: currentGroup[0].left,
|
||||
});
|
||||
}
|
||||
|
||||
return groupedItems;
|
||||
}, [list]);
|
||||
|
||||
return (
|
||||
|
|
@ -52,17 +107,18 @@ const EventRow = React.memo((props: Props) => {
|
|||
{isGraph ? (
|
||||
<PerformanceGraph list={list} />
|
||||
) : _list.length > 0 ? (
|
||||
_list.map((item: any, index: number) => {
|
||||
_list.map((item: { items: any[], left: number, isGrouped: boolean }, index: number) => {
|
||||
const left = item.left
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="absolute"
|
||||
style={{
|
||||
left: `clamp(0%, calc(${item.left}% - 7px), calc(100% - 14px))`,
|
||||
left: `clamp(0%, calc(${left}% - 7px), calc(100% - 14px))`,
|
||||
zIndex: props.zIndex ? props.zIndex : undefined,
|
||||
}}
|
||||
>
|
||||
{props.renderElement ? props.renderElement(item) : null}
|
||||
{props.renderElement ? props.renderElement(item.items, item.isGrouped) : null}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ const OverviewPanelContainer = React.memo((props: Props) => {
|
|||
const [mouseX, setMouseX] = React.useState(0);
|
||||
const [mouseIn, setMouseIn] = React.useState(false);
|
||||
const onClickTrack = (e: any) => {
|
||||
if (e.target.className.includes('ant-popover')) {
|
||||
return;
|
||||
}
|
||||
const p = e.nativeEvent.offsetX / e.target.offsetWidth;
|
||||
const time = Math.max(Math.round(p * endTime), 0);
|
||||
if (time) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
import React from "react";
|
||||
import { EXCEPTIONS, NETWORK } from "App/mstore/uiPlayerStore";
|
||||
import { TYPES } from "App/types/session/event";
|
||||
import { types as issueTypes } from "App/types/session/issue";
|
||||
import { Icon } from "UI";
|
||||
import { Tooltip } from "antd";
|
||||
|
||||
interface CommonProps {
|
||||
item: any;
|
||||
createEventClickHandler: any;
|
||||
}
|
||||
|
||||
export function shortenResourceName(name: string) {
|
||||
return name.length > 100
|
||||
? name.slice(0, 100) + ' ... ' + name.slice(-50)
|
||||
: name
|
||||
}
|
||||
export function NetworkElement({ item, createEventClickHandler }: CommonProps) {
|
||||
const name = item.name || '';
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<div className="">
|
||||
<b>{item.success ? 'Slow resource: ' : '4xx/5xx Error:'}</b>
|
||||
<br />
|
||||
{shortenResourceName(name)}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
onClick={createEventClickHandler(item, NETWORK)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<div className="h-4 w-4 rounded-full bg-red text-white font-bold flex items-center justify-center text-sm">
|
||||
<span>!</span>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export function getFrustration(item: any) {
|
||||
const elData = { name: '', icon: '' };
|
||||
if (item.type === TYPES.CLICK)
|
||||
Object.assign(elData, {
|
||||
name: `User hesitated to click for ${Math.round(
|
||||
item.hesitation / 1000
|
||||
)}s`,
|
||||
icon: 'click-hesitation',
|
||||
});
|
||||
if (item.type === TYPES.INPUT)
|
||||
Object.assign(elData, {
|
||||
name: `User hesitated to enter a value for ${Math.round(
|
||||
item.hesitation / 1000
|
||||
)}s`,
|
||||
icon: 'input-hesitation',
|
||||
});
|
||||
if (item.type === TYPES.CLICKRAGE || item.type === TYPES.TAPRAGE)
|
||||
Object.assign(elData, { name: 'Click Rage', icon: 'click-rage' });
|
||||
if (item.type === TYPES.DEAD_LICK)
|
||||
Object.assign(elData, { name: 'Dead Click', icon: 'emoji-dizzy' });
|
||||
if (item.type === issueTypes.MOUSE_THRASHING)
|
||||
Object.assign(elData, { name: 'Mouse Thrashing', icon: 'cursor-trash' });
|
||||
if (item.type === 'ios_perf_event')
|
||||
Object.assign(elData, { name: item.name, icon: item.icon });
|
||||
|
||||
return elData;
|
||||
}
|
||||
export function FrustrationElement({ item, createEventClickHandler }: CommonProps) {
|
||||
const elData = getFrustration(item);
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<div className="">
|
||||
<b>{elData.name}</b>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
onClick={createEventClickHandler(item, null)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Icon name={elData.icon} color="black" size="16" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export function StackEventElement({ item, createEventClickHandler }: CommonProps) {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<div className="">
|
||||
<b>{item.name || 'Stack Event'}</b>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<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>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export function PerformanceElement({ item, createEventClickHandler }: CommonProps) {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<div className="">
|
||||
<b>{item.type}</b>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<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>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export function ExceptionElement({ item, createEventClickHandler }: CommonProps) {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<div className="">
|
||||
<b>{'Exception'}</b>
|
||||
<br />
|
||||
<span>{item.message}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
onClick={createEventClickHandler(item, 'ERRORS')}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<div className="h-4 w-4 rounded-full bg-red text-white font-bold flex items-center justify-center text-sm">
|
||||
<span>!</span>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,24 +1,40 @@
|
|||
import React from 'react';
|
||||
import { NETWORK, EXCEPTIONS } from 'App/mstore/uiPlayerStore';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import { Icon } from 'UI';
|
||||
import { shortDurationFromMs } from "App/date";
|
||||
import StackEventModal from '../StackEventModal';
|
||||
import ErrorDetailsModal from 'App/components/Dashboard/components/Errors/ErrorDetailsModal';
|
||||
import FetchDetails from 'Shared/FetchDetailsModal';
|
||||
import GraphQLDetailsModal from 'Shared/GraphQLDetailsModal';
|
||||
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||
import { TYPES } from 'App/types/session/event'
|
||||
import { types as issueTypes } from 'App/types/session/issue'
|
||||
import { Tooltip } from 'antd';
|
||||
import { Popover } from 'antd';
|
||||
import {
|
||||
shortenResourceName,
|
||||
NetworkElement,
|
||||
getFrustration,
|
||||
FrustrationElement,
|
||||
StackEventElement,
|
||||
PerformanceElement,
|
||||
ExceptionElement,
|
||||
} from './Dots'
|
||||
|
||||
interface Props {
|
||||
pointer: any;
|
||||
type: 'ERRORS' | 'EVENT' | 'NETWORK' | 'FRUSTRATIONS' | 'EVENTS' | 'PERFORMANCE';
|
||||
type:
|
||||
| 'ERRORS'
|
||||
| 'EVENT'
|
||||
| 'NETWORK'
|
||||
| 'FRUSTRATIONS'
|
||||
| 'EVENTS'
|
||||
| 'PERFORMANCE'
|
||||
noClick?: boolean;
|
||||
fetchPresented?: boolean;
|
||||
isGrouped?: boolean;
|
||||
}
|
||||
const TimelinePointer = React.memo((props: Props) => {
|
||||
const { player } = React.useContext(PlayerContext)
|
||||
const { pointer, type, isGrouped } = props;
|
||||
const { player } = React.useContext(PlayerContext);
|
||||
const item = isGrouped ? pointer : pointer[0]
|
||||
|
||||
const { showModal } = useModal();
|
||||
const createEventClickHandler = (pointer: any, type: any) => (e: any) => {
|
||||
|
|
@ -30,150 +46,162 @@ const TimelinePointer = React.memo((props: Props) => {
|
|||
}
|
||||
|
||||
if (type === 'ERRORS') {
|
||||
showModal(<ErrorDetailsModal errorId={pointer.errorId} />, { right: true, width: 1200 });
|
||||
showModal(<ErrorDetailsModal errorId={pointer.errorId} />, {
|
||||
right: true,
|
||||
width: 1200,
|
||||
});
|
||||
}
|
||||
|
||||
if (type === 'EVENT') {
|
||||
showModal(<StackEventModal event={pointer} />, { right: true, width: 450 });
|
||||
showModal(<StackEventModal event={pointer} />, {
|
||||
right: true,
|
||||
width: 450,
|
||||
});
|
||||
}
|
||||
|
||||
if (type === NETWORK) {
|
||||
if (type === 'NETWORK') {
|
||||
if (pointer.tp === 'graph_ql') {
|
||||
showModal(<GraphQLDetailsModal resource={pointer} />, { right: true, width: 500 });
|
||||
showModal(<GraphQLDetailsModal resource={pointer} />, {
|
||||
right: true,
|
||||
width: 500,
|
||||
});
|
||||
} else {
|
||||
showModal(<FetchDetails resource={pointer} fetchPresented={props.fetchPresented} />, { right: true, width: 500 });
|
||||
showModal(
|
||||
<FetchDetails
|
||||
resource={pointer}
|
||||
fetchPresented={props.fetchPresented}
|
||||
/>,
|
||||
{ right: true, width: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
// props.toggleBottomBlock(type);
|
||||
};
|
||||
|
||||
const renderNetworkElement = (item: any) => {
|
||||
const name = item.name || '';
|
||||
if (isGrouped) {
|
||||
const onClick = createEventClickHandler(item[0], type);
|
||||
return <GroupedIssue type={type} items={item} onClick={onClick} createEventClickHandler={createEventClickHandler} />;
|
||||
}
|
||||
|
||||
if (type === 'NETWORK') {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<div className="">
|
||||
<b>{item.success ? 'Slow resource: ' : '4xx/5xx Error:'}</b>
|
||||
<br />
|
||||
{name.length > 200
|
||||
? name.slice(0, 100) + ' ... ' + name.slice(-50)
|
||||
: name.length > 200
|
||||
? item.name.slice(0, 100) + ' ... ' + item.name.slice(-50)
|
||||
: item.name}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div onClick={createEventClickHandler(item, NETWORK)} className="cursor-pointer">
|
||||
<div className="h-4 w-4 rounded-full bg-red text-white font-bold flex items-center justify-center text-sm">
|
||||
<span>!</span>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<NetworkElement
|
||||
item={item}
|
||||
createEventClickHandler={createEventClickHandler}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderFrustrationElement = (item: any) => {
|
||||
const elData = { name: '', icon: ''}
|
||||
if (item.type === TYPES.CLICK) Object.assign(elData, { name: `User hesitated to click for ${Math.round(item.hesitation/1000)}s`, icon: 'click-hesitation' })
|
||||
if (item.type === TYPES.INPUT) Object.assign(elData, { name: `User hesitated to enter a value for ${Math.round(item.hesitation/1000)}s`, icon: 'input-hesitation' })
|
||||
if (item.type === TYPES.CLICKRAGE || item.type === TYPES.TAPRAGE) Object.assign(elData, { name: 'Click Rage', icon: 'click-rage' })
|
||||
if (item.type === TYPES.DEAD_LICK) Object.assign(elData, { name: 'Dead Click', icon: 'emoji-dizzy' })
|
||||
if (item.type === issueTypes.MOUSE_THRASHING) Object.assign(elData, { name: 'Mouse Thrashing', icon: 'cursor-trash' })
|
||||
if (item.type === 'ios_perf_event') Object.assign(elData, { name: item.name, icon: item.icon })
|
||||
|
||||
}
|
||||
if (type === 'FRUSTRATIONS') {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<div className="">
|
||||
<b>{elData.name}</b>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div onClick={createEventClickHandler(item, null)} className="cursor-pointer">
|
||||
<Icon name={elData.icon} color="black" size="16" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
<FrustrationElement
|
||||
item={item}
|
||||
createEventClickHandler={createEventClickHandler}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderStackEventElement = (item: any) => {
|
||||
}
|
||||
if (type === 'ERRORS') {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<div className="">
|
||||
<b>{item.name || 'Stack Event'}</b>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<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>
|
||||
</Tooltip>
|
||||
<ExceptionElement
|
||||
item={item}
|
||||
createEventClickHandler={createEventClickHandler}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderPerformanceElement = (item: any) => {
|
||||
}
|
||||
if (type === 'EVENTS') {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<div className="">
|
||||
<b>{item.type}</b>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<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>
|
||||
</Tooltip>
|
||||
<StackEventElement
|
||||
item={item}
|
||||
createEventClickHandler={createEventClickHandler}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const renderExceptionElement = (item: any) => {
|
||||
if (type === 'PERFORMANCE') {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<div className="">
|
||||
<b>{'Exception'}</b>
|
||||
<br />
|
||||
<span>{item.message}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div onClick={createEventClickHandler(item, 'ERRORS')} className="cursor-pointer">
|
||||
<div className="h-4 w-4 rounded-full bg-red text-white font-bold flex items-center justify-center text-sm">
|
||||
<span>!</span>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<PerformanceElement
|
||||
item={item}
|
||||
createEventClickHandler={createEventClickHandler}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const render = () => {
|
||||
const { pointer, type } = props;
|
||||
if (type === 'NETWORK') {
|
||||
return renderNetworkElement(pointer);
|
||||
}
|
||||
if (type === 'FRUSTRATIONS') {
|
||||
return renderFrustrationElement(pointer);
|
||||
}
|
||||
if (type === 'ERRORS') {
|
||||
return renderExceptionElement(pointer);
|
||||
}
|
||||
if (type === 'EVENTS') {
|
||||
return renderStackEventElement(pointer);
|
||||
}
|
||||
|
||||
if (type === 'PERFORMANCE') {
|
||||
return renderPerformanceElement(pointer);
|
||||
}
|
||||
};
|
||||
return <div>{render()}</div>;
|
||||
return <div>unknown type</div>;
|
||||
});
|
||||
|
||||
function GroupedIssue({
|
||||
type,
|
||||
items,
|
||||
onClick,
|
||||
createEventClickHandler,
|
||||
}: {
|
||||
type: string;
|
||||
items: Record<string, any>[];
|
||||
onClick: () => void;
|
||||
createEventClickHandler: any;
|
||||
}) {
|
||||
const subStr = {
|
||||
NETWORK: 'Network Issues',
|
||||
ERRORS: 'Errors',
|
||||
EVENTS: 'Events',
|
||||
FRUSTRATIONS: 'Frustrations',
|
||||
};
|
||||
const title = `${items.length} ${subStr[type]} Observed`;
|
||||
|
||||
return (
|
||||
<Popover
|
||||
placement={'right'}
|
||||
title={title}
|
||||
content={
|
||||
<div style={{ maxHeight: 160, overflowY: 'auto' }}>
|
||||
{items.map((pointer) => (
|
||||
<div
|
||||
key={pointer.time}
|
||||
onClick={createEventClickHandler(pointer, type)}
|
||||
className={'flex items-center gap-2 mb-1 cursor-pointer border-b border-transparent hover:border-gray-lightest'}
|
||||
>
|
||||
<div className={'text-disabled-text'}>@{shortDurationFromMs(pointer.time)}</div>
|
||||
<RenderLineData type={type} item={pointer} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={
|
||||
'h-5 w-5 cursor-pointer rounded-full bg-red text-white font-bold flex items-center justify-center text-sm'
|
||||
}
|
||||
>
|
||||
{items.length}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
function RenderLineData({ item, type }: any) {
|
||||
if (type === 'FRUSTRATIONS') {
|
||||
const elData = getFrustration(item);
|
||||
return <>
|
||||
<div><Icon name={elData.icon} color="black" size="16" /></div>
|
||||
<div>{elData.name}</div>
|
||||
</>
|
||||
}
|
||||
if (type === 'NETWORK') {
|
||||
const name = item.success ? 'Slow resource' : '4xx/5xx Error';
|
||||
return <>
|
||||
<div>{name}</div>
|
||||
<div>{shortenResourceName(item.name)}</div>
|
||||
</>
|
||||
}
|
||||
if (type === 'EVENTS') {
|
||||
return <div>{item.name || 'Stack Event'}</div>
|
||||
}
|
||||
if (type === 'PERFORMANCE') {
|
||||
return <div>{item.type}</div>
|
||||
}
|
||||
if (type === 'ERRORS') {
|
||||
return <div>{item.message}</div>
|
||||
}
|
||||
return <div>{JSON.stringify(item)}</div>
|
||||
}
|
||||
|
||||
export default TimelinePointer;
|
||||
|
|
|
|||
80
frontend/app/components/Session_/OverviewPanel/fakeData.ts
Normal file
80
frontend/app/components/Session_/OverviewPanel/fakeData.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
const zoomStartTime = 100
|
||||
// Generate fake fetchList data for NETWORK
|
||||
const fetchList: any[] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const statusOptions = [200, 200, 200, 404, 500]; // Higher chance of 200
|
||||
const status = statusOptions[Math.floor(Math.random() * statusOptions.length)];
|
||||
const isRed = status >= 500;
|
||||
const isYellow = status >= 400 && status < 500;
|
||||
const resource = {
|
||||
time: zoomStartTime + i * 1000 + Math.floor(Math.random() * 500), // Incremental time with randomness
|
||||
name: `https://api.example.com/resource/${i}`,
|
||||
status: status,
|
||||
isRed: isRed,
|
||||
isYellow: isYellow,
|
||||
success: status < 400,
|
||||
tp: Math.random() > 0.5 ? 'graph_ql' : 'fetch',
|
||||
// Additional properties used by your component
|
||||
method: 'GET',
|
||||
duration: Math.floor(Math.random() * 3000) + 500, // Duration between 500ms to 3.5s
|
||||
};
|
||||
fetchList.push(resource);
|
||||
}
|
||||
// Generate fake exceptionsList data for ERRORS
|
||||
const exceptionsList: any[] = [];
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const exception = {
|
||||
time: zoomStartTime + i * 2000 + Math.floor(Math.random() * 1000),
|
||||
message: `Error message ${i}`,
|
||||
errorId: `error-${i}`,
|
||||
type: 'ERRORS',
|
||||
// Additional properties if needed
|
||||
stackTrace: `Error at function ${i} in file${i}.js`,
|
||||
};
|
||||
exceptionsList.push(exception);
|
||||
}
|
||||
// Generate fake eventsList data for EVENTS
|
||||
const eventsList: any[] = [];
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const event = {
|
||||
time: zoomStartTime + i * 1500 + Math.floor(Math.random() * 500),
|
||||
name: `Custom Event ${i}`,
|
||||
type: 'EVENTS',
|
||||
// Additional properties if needed
|
||||
details: `Details about event ${i}`,
|
||||
};
|
||||
eventsList.push(event);
|
||||
}
|
||||
// Generate fake performanceChartData data for PERFORMANCE
|
||||
const performanceChartData: any[] = [];
|
||||
const performanceTypes = ['SLOW_PAGE_LOAD', 'HIGH_MEMORY_USAGE'];
|
||||
for (let i = 0; i < 30; i++) {
|
||||
const performanceEvent = {
|
||||
time: zoomStartTime + i * 3000 + Math.floor(Math.random() * 1500),
|
||||
type: performanceTypes[Math.floor(Math.random() * performanceTypes.length)],
|
||||
// Additional properties if needed
|
||||
value: Math.floor(Math.random() * 1000) + 500, // Random value
|
||||
};
|
||||
performanceChartData.push(performanceEvent);
|
||||
}
|
||||
// Generate fake frustrationsList data for FRUSTRATIONS
|
||||
const frustrationsList: any[] = [];
|
||||
const frustrationEventTypes = ['CLICK', 'INPUT', 'CLICKRAGE', 'DEAD_CLICK', 'MOUSE_THRASHING'];
|
||||
for (let i = 0; i < 70; i++) {
|
||||
const frustrationEvent = {
|
||||
time: zoomStartTime + i * 1200 + Math.floor(Math.random() * 600),
|
||||
type: frustrationEventTypes[Math.floor(Math.random() * frustrationEventTypes.length)],
|
||||
hesitation: Math.floor(Math.random() * 5000) + 1000, // 1s to 6s
|
||||
// Additional properties if needed
|
||||
details: `Frustration event ${i}`,
|
||||
};
|
||||
frustrationsList.push(frustrationEvent);
|
||||
}
|
||||
|
||||
export const resources = {
|
||||
NETWORK: fetchList.filter((r: any) => r.status >= 400 || r.isRed || r.isYellow),
|
||||
ERRORS: exceptionsList,
|
||||
EVENTS: eventsList,
|
||||
PERFORMANCE: performanceChartData,
|
||||
FRUSTRATIONS: frustrationsList,
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue