feat(ui) - fetch error nav, and highlight row, assist tabs

This commit is contained in:
Shekar Siri 2021-12-24 13:06:32 +05:30
parent edd6cc737e
commit bd957f97f8
19 changed files with 246 additions and 247 deletions

View file

@ -0,0 +1,68 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { applyFilter, addAttribute } from 'Duck/filters';
import { fetchList } from 'Duck/sessions';
import { KEYS } from 'Types/filter/customFilter';
import { Link } from 'UI';
import Filter from 'Types/filter';
import { List } from 'immutable';
import Counter from 'App/components/shared/SessionItem/Counter';
import { fetchLiveList } from 'Duck/sessions';
import { session as sessionRoute } from 'App/routes';
import { session } from 'App/components/Session_/session.css';
const RowItem = ({ startedAt, sessionId }) => {
return (
<Link to={ sessionRoute(sessionId) }>
<div className="flex justify-between p-2 cursor-pointer">
Tab1
<Counter startTime={startedAt} />
</div>
</Link>
);
}
interface Props {
list: List<any>,
session: any,
fetchLiveList: () => void,
applyFilter: () => void,
filters: Filter
addAttribute: (obj) => void,
loading: boolean
}
const AssistTabs = React.memo((props: Props) => {
const [showMenu, setShowMenu] = useState(false)
useEffect(() => {
if (!props.loading && props.list.size === 0) {
props.fetchLiveList();
}
}, [props.list])
return (
<div className="relative mr-4">
<div className="p-2 cursor-pointer" onClick={() => setShowMenu(!showMenu)}>Active Tabs</div>
{showMenu && (
<div
className="border z-10 absolute bg-white rounded shadow right-0"
style={{ minWidth: "180px"}}
>
{props.list.map((item, index) => (
<RowItem key={index} startedAt={item.startedAt} sessionId={item.sessionId} />
))}
</div>
)}
</div>
);
});
export default connect(state => {
const session = state.getIn([ 'sessions', 'current' ]);
return {
loading: state.getIn([ 'sessions', 'loading' ]),
list: state.getIn(['sessions', 'liveSessions']).filter(i => i.userId === session.userId),
session,
filters: state.getIn([ 'filters', 'appliedFilter' ]),
}
}, { applyFilter, addAttribute, fetchLiveList })(AssistTabs);

View file

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

View file

@ -119,7 +119,7 @@ export default connect(state => ({
activeFlow: state.getIn([ 'filters', 'activeFlow' ]),
captureRate: state.getIn(['watchdogs', 'captureRate']),
filters: state.getIn([ 'filters', 'appliedFilter' ]),
sessionsLoading: state.getIn([ 'sessions', 'loading' ]),
sessionsLoading: state.getIn([ 'sessions', 'fetchLiveListRequest', 'loading' ]),
}), {
fetchWatchdogStatus, setActiveFlow, clearEvents, setActiveTab, fetchSessionList
})(SessionsMenu);

View file

@ -31,7 +31,7 @@ const InitLoader = connectPlayer(state => ({
}))(Loader);
function WebPlayer ({ showAssist, session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt, loadingCredentials, assistCredendials, request }) {
const WebPlayer = React.memo(({ showAssist, session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt, loadingCredentials, assistCredendials, request }) => {
useEffect(() => {
if (!loadingCredentials) {
initPlayer(session, jwt, assistCredendials);
@ -60,7 +60,7 @@ function WebPlayer ({ showAssist, session, toggleFullscreen, closeBottomBlock, l
</InitLoader>
</PlayerProvider>
);
}
});
export default withRequest({
initialData: null,

View file

@ -6,6 +6,7 @@ import { fetchList as fetchSlackList } from 'Duck/integrations/slack';
import { Link, NoContent, Loader } from 'UI';
import { sessions as sessionsRoute } from 'App/routes';
import withPermissions from 'HOCs/withPermissions'
import { fetchLiveList } from 'Duck/sessions';
import LivePlayer from './LivePlayer';
import WebPlayer from './WebPlayer';
@ -20,6 +21,8 @@ function Session({
session,
fetchSession,
fetchSlackList,
fetchLiveList,
filters
}) {
usePageTitle("OpenReplay Session Player");
useEffect(() => {
@ -36,6 +39,12 @@ function Session({
}
},[ sessionId ]);
// useEffect(() => {
// if (session && session.live) {
// fetchLiveList(filters.toJS())
// }
// }, [session])
return (
<NoContent
show={ hasErrors }
@ -64,8 +73,10 @@ export default withPermissions(['SESSION_REPLAY'], '', true)(connect((state, pro
loading: state.getIn([ 'sessions', 'loading' ]),
hasErrors: !!state.getIn([ 'sessions', 'errors' ]),
session: state.getIn([ 'sessions', 'current' ]),
filters: state.getIn([ 'filters', 'appliedFilter' ]),
};
}, {
fetchSession,
fetchSlackList,
fetchLiveList,
})(Session));

View file

@ -1,30 +1,51 @@
//import cn from 'classnames';
import { getRE } from 'App/utils';
import { Label, NoContent, Input, SlideModal, CloseButton } from 'UI';
import { connectPlayer, pause } from 'Player';
import { connectPlayer, pause, jump } from 'Player';
import Autoscroll from '../Autoscroll';
import BottomBlock from '../BottomBlock';
import TimeTable from '../TimeTable';
import FetchDetails from './FetchDetails';
import { renderName, renderDuration } from '../Network';
import { connect } from 'react-redux';
@connectPlayer(state => ({
list: state.fetchList,
}))
@connect(state => ({
timelinePointer: state.getIn(['sessions', 'timelinePointer']),
}))
export default class Fetch extends React.PureComponent {
state = {
filter: "",
filteredList: this.props.list,
current: null,
currentIndex: 0,
showFetchDetails: false,
hasNextError: false,
hasPreviousError: false,
}
onFilterChange = (e, { value }) => this.setState({ filter: value })
onFilterChange = (e, { value }) => {
const { list } = this.props;
const filterRE = getRE(value, 'i');
const filtered = list
.filter((r) => filterRE.test(r.name) || filterRE.test(r.url) || filterRE.test(r.method) || filterRE.test(r.status));
this.setState({ filter: value, filteredList: value ? filtered : list, currentIndex: 0 });
}
setCurrent = (item, index) => {
pause()
jump(item.time)
this.setState({ current: item, currentIndex: index });
}
closeModal = () => this.setState({ current: null})
onRowClick = (item, index) => {
pause()
this.setState({ current: item, currentIndex: index, showFetchDetails: true });
}
closeModal = () => this.setState({ current: null, showFetchDetails: false });
nextClickHander = () => {
const { list } = this.props;
@ -33,6 +54,7 @@ export default class Fetch extends React.PureComponent {
if (currentIndex === list.length - 1) return;
const newIndex = currentIndex + 1;
this.setCurrent(list[newIndex], newIndex);
this.setState({ showFetchDetails: true });
}
prevClickHander = () => {
@ -42,15 +64,24 @@ export default class Fetch extends React.PureComponent {
if (currentIndex === 0) return;
const newIndex = currentIndex - 1;
this.setCurrent(list[newIndex], newIndex);
this.setState({ showFetchDetails: true });
}
static getDerivedStateFromProps(nextProps, prevState) {
const { filteredList } = prevState;
if (nextProps.timelinePointer) {
let activeItem = filteredList.find((r) => r.time >= nextProps.timelinePointer.time);
activeItem = activeItem || filteredList[filteredList.length - 1];
return {
current: activeItem,
currentIndex: filteredList.indexOf(activeItem),
};
}
}
render() {
const { list } = this.props;
const { filter, current, currentIndex } = this.state;
const filterRE = getRE(filter, 'i');
const filtered = list
.filter((r) => filterRE.test(r.name) || filterRE.test(r.url) || filterRE.test(r.method) || filterRE.test(r.status));
// const { list } = this.props;
const { current, currentIndex, showFetchDetails, filteredList } = this.state;
return (
<React.Fragment>
<SlideModal
@ -73,14 +104,14 @@ export default class Fetch extends React.PureComponent {
</div>
</div>
}
isDisplayed={ current != null }
content={ current &&
isDisplayed={ current != null && showFetchDetails }
content={ current && showFetchDetails &&
<FetchDetails
resource={ current }
nextClick={this.nextClickHander}
prevClick={this.prevClickHander}
first={currentIndex === 0}
last={currentIndex === filtered.length - 1}
last={currentIndex === filteredList.length - 1}
/>
}
onClose={ this.closeModal }
@ -88,25 +119,31 @@ export default class Fetch extends React.PureComponent {
<BottomBlock>
<BottomBlock.Header>
<h4 className="text-lg">Fetch</h4>
<Input
className="input-small"
placeholder="Filter"
icon="search"
iconPosition="left"
name="filter"
onChange={ this.onFilterChange }
/>
<div className="flex items-center">
{/* <div className="flex items-center mr-3 text-sm uppercase">
<div className="p-2 cursor-pointer" onClick={this.goToPrevError}>Prev</div>
<div className="p-2 cursor-pointer" onClick={this.goToNextError}>Next</div>
</div> */}
<Input
className="input-small"
placeholder="Filter"
icon="search"
iconPosition="left"
name="filter"
onChange={ this.onFilterChange }
/>
</div>
</BottomBlock.Header>
<BottomBlock.Content>
<NoContent
size="small"
show={ filtered.length === 0}
show={ filteredList.length === 0}
>
<TimeTable
rows={ filtered }
onRowClick={ this.setCurrent }
rows={ filteredList }
onRowClick={ this.onRowClick }
hoverable
// navigation
navigation
activeIndex={currentIndex}
>
{[
@ -120,7 +157,7 @@ export default class Fetch extends React.PureComponent {
width: 60,
}, {
label: "Name",
width: 130,
width: 180,
render: renderName,
},
{

View file

@ -3,14 +3,9 @@ import { connectPlayer, jump, pause } from 'Player';
import { QuestionMarkHint, Popup, Tabs, Input } from 'UI';
import { getRE } from 'App/utils';
import { TYPES } from 'Types/session/resource';
import { formatBytes } from 'App/utils';
import { formatMs } from 'App/date';
import TimeTable from '../TimeTable';
import BottomBlock from '../BottomBlock';
import InfoLine from '../BottomBlock/InfoLine';
import stl from './network.css';
import NetworkContent from './NetworkContent';
import { connect } from 'react-redux';
const ALL = 'ALL';
const XHR = 'xhr';
@ -28,73 +23,24 @@ const TAB_TO_TYPE_MAP = {
[ MEDIA ]: TYPES.MEDIA,
[ OTHER ]: TYPES.OTHER
}
const TABS = [ ALL, XHR, JS, CSS, IMG, MEDIA, OTHER ].map(tab => ({
text: tab,
key: tab,
}));
const DOM_LOADED_TIME_COLOR = "teal";
const LOAD_TIME_COLOR = "red";
export function renderName(r) {
return (
<Popup
trigger={ <div className={ stl.popupNameTrigger }>{ r.name }</div> }
content={ <div className={ stl.popupNameContent }>{ r.url }</div> }
size="mini"
position="right center"
/>
);
}
const renderXHRText = () => (
<span className="flex items-center">
{XHR}
<QuestionMarkHint
onHover
content={
<>
Use our <a className="color-teal underline" target="_blank" href="https://docs.openreplay.com/plugins/fetch">Fetch plugin</a>
{' to capture HTTP requests and responses, including status codes and bodies.'} <br/>
We also provide <a className="color-teal underline" target="_blank" href="https://docs.openreplay.com/plugins/graphql">support for GraphQL</a>
{' for easy debugging of your queries.'}
</>
}
className="ml-1"
/>
</span>
);
function renderSize(r) {
let triggerText;
let content;
if (r.decodedBodySize == null) {
triggerText = "x";
content = "Not captured";
} else {
const headerSize = r.headerSize || 0;
const encodedSize = r.encodedBodySize || 0;
const transferred = headerSize + encodedSize;
const showTransferred = r.headerSize != null;
triggerText = formatBytes(r.decodedBodySize);
content = (
<ul>
{ showTransferred &&
<li>{`${formatBytes( r.encodedBodySize + headerSize )} transfered over network`}</li>
}
<li>{`Resource size: ${formatBytes(r.decodedBodySize)} `}</li>
</ul>
);
}
return (
<Popup
trigger={ <div>{ triggerText }</div> }
content={ content }
size="mini"
position="right center"
/>
<div className="flex w-full relative items-center">
<Popup
trigger={ <div className={ stl.popupNameTrigger }>{ r.name }</div> }
content={ <div className={ stl.popupNameContent }>{ r.url }</div> }
size="mini"
position="right center"
/>
<div
className="absolute right-0 text-xs uppercase p-2 color-gray-500 hover:color-black"
onClick={ (e) => {
e.stopPropagation();
jump(r.time)
}}
>Jump</div>
</div>
);
}
@ -130,14 +76,18 @@ export function renderDuration(r) {
resources: state.resourceList,
domContentLoadedTime: state.domContentLoadedTime,
loadTime: state.loadTime,
time: state.time,
// time: state.time,
playing: state.playing,
domBuildingTime: state.domBuildingTime,
fetchPresented: state.fetchList.length > 0,
}))
@connect(state => ({
timelinePointer: state.getIn(['sessions', 'timelinePointer']),
}))
export default class Network extends React.PureComponent {
state = {
filter: '',
filteredList: this.props.resources,
activeTab: ALL,
currentIndex: 0
}
@ -149,7 +99,30 @@ export default class Network extends React.PureComponent {
}
onTabClick = activeTab => this.setState({ activeTab })
onFilterChange = (e, { value }) => this.setState({ filter: value })
onFilterChange = (e, { value }) => {
const { resources } = this.props;
const filterRE = getRE(value, 'i');
const filtered = resources.filter(({ type, name }) =>
filterRE.test(name) && (activeTab === ALL || type === TAB_TO_TYPE_MAP[ activeTab ]));
this.setState({ filter: value, filteredList: value ? filtered : resources, currentIndex: 0 });
}
// onFilterChange = (e, { value }) => this.setState({ filter: value })
componentDidUpdate() {
console.log('test')
}
static getDerivedStateFromProps(nextProps, prevState) {
const { filteredList } = prevState;
if (nextProps.timelinePointer) {
const activeItem = filteredList.find((r) => r.time >= nextProps.timelinePointer.time);
return {
currentIndex: activeItem ? filteredList.indexOf(activeItem) : filteredList.length - 1,
};
}
}
render() {
const {
@ -159,50 +132,23 @@ export default class Network extends React.PureComponent {
loadTime,
domBuildingTime,
fetchPresented,
time,
// time,
playing
} = this.props;
const { filter, activeTab, currentIndex } = this.state;
const filterRE = getRE(filter, 'i');
let filtered = resources.filter(({ type, name }) =>
filterRE.test(name) && (activeTab === ALL || type === TAB_TO_TYPE_MAP[ activeTab ]));
// const referenceLines = [];
// if (domContentLoadedTime != null) {
// referenceLines.push({
// time: domContentLoadedTime,
// color: DOM_LOADED_TIME_COLOR,
// })
// }
// if (loadTime != null) {
// referenceLines.push({
// time: loadTime,
// color: LOAD_TIME_COLOR,
// })
// }
//
// let tabs = TABS;
// if (!fetchPresented) {
// tabs = TABS.map(tab => tab.key === XHR
// ? {
// text: renderXHRText(),
// key: XHR,
// }
// : tab
// );
// }
const resourcesSize = filtered.reduce((sum, { decodedBodySize }) => sum + (decodedBodySize || 0), 0);
const transferredSize = filtered
const { filter, activeTab, currentIndex, filteredList } = this.state;
// const filterRE = getRE(filter, 'i');
// let filtered = resources.filter(({ type, name }) =>
// filterRE.test(name) && (activeTab === ALL || type === TAB_TO_TYPE_MAP[ activeTab ]));
const resourcesSize = filteredList.reduce((sum, { decodedBodySize }) => sum + (decodedBodySize || 0), 0);
const transferredSize = filteredList
.reduce((sum, { headerSize, encodedBodySize }) => sum + (headerSize || 0) + (encodedBodySize || 0), 0);
return (
<React.Fragment>
<NetworkContent
// {...this.props }
time = { time }
// time = { time }
location = { location }
resources = { resources }
resources = { filteredList }
domContentLoadedTime = { domContentLoadedTime }
loadTime = { loadTime }
domBuildingTime = { domBuildingTime }
@ -210,91 +156,8 @@ export default class Network extends React.PureComponent {
resourcesSize={resourcesSize}
transferredSize={transferredSize}
onRowClick={ this.onRowClick }
currentIndex={playing ? null : currentIndex}
currentIndex={currentIndex}
/>
{/* <BottomBlock>
<BottomBlock.Header>
<Tabs
className="uppercase"
tabs={ tabs }
active={ activeTab }
onClick={ this.onTabClick }
border={ false }
/>
<Input
className="input-small"
placeholder="Filter by Name"
icon="search"
iconPosition="left"
name="filter"
onChange={ this.onFilterChange }
/>
</BottomBlock.Header>
<BottomBlock.Content>
<InfoLine>
<InfoLine.Point label={ filtered.length } value=" requests" />
<InfoLine.Point
label={ formatBytes(transferredSize) }
value="transferred"
display={ transferredSize > 0 }
/>
<InfoLine.Point
label={ formatBytes(resourcesSize) }
value="resources"
display={ resourcesSize > 0 }
/>
<InfoLine.Point
label="DOM Building Time"
value={ formatMs(domBuildingTime)}
display={ domBuildingTime != null }
/>
<InfoLine.Point
label="DOMContentLoaded"
value={ formatMs(domContentLoadedTime)}
display={ domContentLoadedTime != null }
dotColor={ DOM_LOADED_TIME_COLOR }
/>
<InfoLine.Point
label="Load"
value={ formatMs(loadTime)}
display={ loadTime != null }
dotColor={ LOAD_TIME_COLOR }
/>
</InfoLine>
<TimeTable
rows={ filtered }
referenceLines={referenceLines}
renderPopup
navigation
>
{[
{
label: "Status",
dataKey: 'status',
width: 70,
}, {
label: "Type",
dataKey: 'type',
width: 60,
}, {
label: "Name",
width: 130,
render: renderName,
},
{
label: "Size",
width: 60,
render: renderSize,
},
{
label: "Time",
width: 80,
render: renderDuration,
}
]}
</TimeTable>
</BottomBlock.Content>
</BottomBlock> */}
</React.Fragment>
);
}

View file

@ -22,7 +22,7 @@
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: 100%;
max-width: 80%;
width: fit-content;
}
.popupNameContent {

View file

@ -7,6 +7,7 @@ import TimeTracker from './TimeTracker';
import { ReduxTime } from './Time';
import stl from './timeline.css';
import { TYPES } from 'Types/session/event';
import { setTimelinePointer } from 'Duck/sessions';
const getPointerIcon = (type) => {
// exception,
@ -69,7 +70,7 @@ const getPointerIcon = (type) => {
state.getIn([ 'sessions', 'current', 'clickRageTime' ]),
returningLocationTime: state.getIn([ 'sessions', 'current', 'returningLocation' ]) &&
state.getIn([ 'sessions', 'current', 'returningLocationTime' ]),
}))
}), { setTimelinePointer })
export default class Timeline extends React.PureComponent {
seekProgress = (e) => {
const { endTime } = this.props;
@ -78,9 +79,10 @@ export default class Timeline extends React.PureComponent {
this.props.jump(time);
}
createEventClickHandler = time => (e) => {
createEventClickHandler = pointer => (e) => {
e.stopPropagation();
this.props.jump(time)
this.props.jump(pointer.time);
this.props.setTimelinePointer(pointer);
}
componentDidMount() {
@ -144,7 +146,7 @@ export default class Timeline extends React.PureComponent {
//width: `${ 2000 * scale }%`
} }
className={ stl.clickRage }
onClick={ this.createEventClickHandler(iss.time) }
onClick={ this.createEventClickHandler(iss) }
>
<TimelinePointer
icon={iss.icon}
@ -165,7 +167,7 @@ export default class Timeline extends React.PureComponent {
//width: `${ 2000 * scale }%`
} }
className={ stl.clickRage }
onClick={ this.createEventClickHandler(e.time) }
onClick={ this.createEventClickHandler(e) }
>
<TimelinePointer
icon={getPointerIcon('click_rage')}
@ -259,7 +261,7 @@ export default class Timeline extends React.PureComponent {
key={ e.key }
className={ cn(stl.markup, stl.error) }
style={ { left: `${ e.time * scale }%`, top: '-30px' } }
onClick={ this.createEventClickHandler(e.time) }
onClick={ this.createEventClickHandler(e) }
>
<TimelinePointer
icon={getPointerIcon('exception')}
@ -305,7 +307,7 @@ export default class Timeline extends React.PureComponent {
//[ stl.info ]: !l.isYellow() && !l.isRed(),
}) }
style={ { left: `${ l.time * scale }%`, top: '-30px' } }
onClick={ this.createEventClickHandler(l.time) }
onClick={ this.createEventClickHandler(l) }
>
<TimelinePointer
icon={getPointerIcon('log')}
@ -360,7 +362,7 @@ export default class Timeline extends React.PureComponent {
[ stl.warning ]: r.isYellow(),
}) }
style={ { left: `${ r.time * scale }%`, top: '-30px' } }
onClick={ this.createEventClickHandler(r.time) }
onClick={ this.createEventClickHandler(r) }
>
<TimelinePointer
icon={getPointerIcon('resource')}
@ -407,7 +409,7 @@ export default class Timeline extends React.PureComponent {
key={ e.key }
className={ cn(stl.markup, stl.error) }
style={ { left: `${ e.time * scale }%`, top: '-30px' } }
onClick={ this.createEventClickHandler(e.time) }
onClick={ this.createEventClickHandler(e) }
>
<TimelinePointer
icon={getPointerIcon('fetch')}
@ -448,7 +450,7 @@ export default class Timeline extends React.PureComponent {
key={ e.key }
className={ cn(stl.markup, stl.error) }
style={ { left: `${ e.time * scale }%`, top: '-30px' } }
onClick={ this.createEventClickHandler(e.time) }
onClick={ this.createEventClickHandler(e) }
>
<TimelinePointer
icon={getPointerIcon('stack')}

View file

@ -15,6 +15,7 @@ import cls from './playerBlockHeader.css';
import Issues from './Issues/Issues';
import Autoplay from './Autoplay';
import AssistActions from '../Assist/components/AssistActions';
import AssistTabs from '../Assist/components/AssistTabs';
const SESSIONS_ROUTE = sessionsRoute();
@ -115,6 +116,7 @@ export default class PlayerBlockHeader extends React.PureComponent {
<HeaderInfo icon={ osIcon(userOs) } label={ userOs } />
<div className='ml-auto flex items-center'>
{ live && <AssistTabs />}
{ live && <AssistActions isLive userId={userId} /> }
{ !live && (
<>

View file

@ -135,7 +135,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
...computeTimeLine(this.props.rows, this.state.firstVisibleRowIndex, this.visibleCount),
});
}
if (this.props.activeIndex && prevProps.activeIndex !== this.props.activeIndex && this.scroller.current != null) {
if (this.props.activeIndex >= 0 && prevProps.activeIndex !== this.props.activeIndex && this.scroller.current != null) {
this.scroller.current.scrollToRow(this.props.activeIndex);
}
}
@ -168,7 +168,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
<div
style={ rowStyle }
key={ key }
className={ cn('border-b border-color-gray-light-shade', stl.row, { [ stl.hoverable ]: hoverable, "error color-red": !!row.isRed && row.isRed(), 'cursor-pointer' : typeof onRowClick === "function", [stl.activeRow] : activeIndex === index, 'color-white' : activeIndex === index }) }
className={ cn('border-b border-color-gray-light-shade', stl.row, { [ stl.hoverable ]: hoverable, "error color-red": !!row.isRed && row.isRed(), 'cursor-pointer' : typeof onRowClick === "function", [stl.activeRow] : activeIndex === index }) }
onClick={ typeof onRowClick === "function" ? () => onRowClick(row, index) : null }
id="table-row"
>
@ -223,7 +223,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
navigation=false,
referenceLines = [],
additionalHeight = 0,
activeIndex
activeIndex,
} = this.props;
const {
timewidth,
@ -247,7 +247,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
return (
<div className={ cn(className, "relative") }>
{ navigation &&
<div className={ cn(autoscrollStl.navButtons, "flex items-center") } style={{ top: '-33px', right: '30px' }} >
<div className={ cn(autoscrollStl.navButtons, "flex items-center") } >
<IconButton
size="small"
icon="chevron-up"

View file

@ -99,8 +99,6 @@ $offset: 10px;
}
}
.activeRow {
background-color: $teal;
background-color: rgba(54, 108, 217, 0.1);
}

View file

@ -12,8 +12,8 @@
.navButtons {
position: absolute;
right: 40px;
top: 10px;
right: 260px;
top: -34px;
}

View file

@ -3,7 +3,7 @@ import { Duration } from 'luxon';
interface Props {
startTime: any,
className: string
className?: string
}
function Counter({ startTime, className }: Props) {

View file

@ -7,6 +7,7 @@
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
height: 36px;
font-size: 14px;

View file

@ -25,6 +25,7 @@ const SET_EVENT_QUERY = 'sessions/SET_EVENT_QUERY';
const SET_AUTOPLAY_VALUES = 'sessions/SET_AUTOPLAY_VALUES';
const TOGGLE_CHAT_WINDOW = 'sessions/TOGGLE_CHAT_WINDOW';
const SET_FUNNEL_PAGE_FLAG = 'sessions/SET_FUNNEL_PAGE_FLAG';
const SET_TIMELINE_POINTER = 'sessions/SET_TIMELINE_POINTER';
const SET_ACTIVE_TAB = 'sessions/SET_ACTIVE_TAB';
@ -57,6 +58,7 @@ const initialState = Map({
insightFilters: defaultDateFilters,
host: '',
funnelPage: Map(),
timelinePointer: null,
});
const reducer = (state = initialState, action = {}) => {
@ -242,13 +244,16 @@ const reducer = (state = initialState, action = {}) => {
return state.set('insights', List(action.data).sort((a, b) => b.count - a.count));
case SET_FUNNEL_PAGE_FLAG:
return state.set('funnelPage', action.funnelPage ? Map(action.funnelPage) : false);
case SET_TIMELINE_POINTER:
return state.set('timelinePointer', action.pointer);
default:
return state;
}
};
export default withRequestState({
_: [ FETCH, FETCH_LIST, FETCH_LIVE_LIST ],
_: [ FETCH, FETCH_LIST ],
fetchLiveListRequest: FETCH_LIVE_LIST,
fetchFavoriteListRequest: FETCH_FAVORITE_LIST,
toggleFavoriteRequest: TOGGLE_FAVORITE,
fetchErrorStackList: FETCH_ERROR_STACK,
@ -262,10 +267,10 @@ function init(session) {
}
}
export const fetchList = (params = {}, clear = false) => (dispatch, getState) => {
export const fetchList = (params = {}, clear = false, live = false) => (dispatch, getState) => {
const activeTab = getState().getIn([ 'sessions', 'activeTab' ]);
return dispatch(activeTab && activeTab.type === 'live' ? {
return dispatch((activeTab && activeTab.type === 'live' || live )? {
types: FETCH_LIVE_LIST.toArray(),
call: client => client.post('/assist/sessions', params),
} : {
@ -376,3 +381,9 @@ export function setFunnelPage(funnelPage) {
}
}
export function setTimelinePointer(pointer) {
return {
type: SET_TIMELINE_POINTER,
pointer
}
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 204.7 200.9" xmlns="http://www.w3.org/2000/svg">
<title>Fedora logo (2021)</title>
<path d="m102.7 1.987c-55.41 0-100.3 44.21-100.4 98.79h-0.01773v76.47h0.01773c0.02659 12.38 10.22 22.4 22.8 22.4h77.58c55.42-0.0349 100.3-44.24 100.3-98.79 8e-5 -54.58-44.91-98.79-100.4-98.79zm20.39 40.68c16.85 0 32.76 12.7 32.76 30.23 0 1.625 0.01 3.252-0.26 5.095-0.4668 4.662-4.794 8.012-9.505 7.355-4.711-0.6649-7.909-5.07-7.037-9.679 0.0799-0.526 0.1083-1.352 0.1083-2.772 0-9.938-8.257-13.77-16.06-13.77-7.805 0-14.84 6.462-14.85 13.77 0.1349 8.455 0 16.84 0 25.29l14.49-0.1067c11.31-0.2306 11.44 16.54 0.1305 16.46l-14.61 0.1067c-0.0354 6.801 0.0532 5.571 0.0178 8.996 0 0 0.1225 8.318-0.1296 14.62-1.749 18.52-17.76 33.32-37 33.32-20.4 0-37.2-16.41-37.2-36.54 0.6124-20.7 17.38-36.99 38.5-36.8l11.78-0.08737v16.43l-11.78 0.1066h-0.06216c-11.6 0.3382-21.55 8.1-21.74 20.34 0 11.15 9.148 20.08 20.5 20.08 11.34 0 20.42-8.124 20.42-20.06l-0.01772-62.23c0.0058-1.155 0.04435-2.073 0.1731-3.347 1.914-15.22 15.74-26.82 31.39-26.82z" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -34,8 +34,8 @@ export default Record({
startDate,
endDate,
sort: undefined,
order: undefined,
sort: 'startTs',
order: 'desc',
viewed: undefined,
consoleLogCount: undefined,