change(ui) - live sessions maintain query params
This commit is contained in:
parent
e4b77669da
commit
ef679a275f
3 changed files with 178 additions and 181 deletions
|
|
@ -1,44 +1,42 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import {
|
||||
Link,
|
||||
Icon,
|
||||
} from 'UI';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Link, Icon } from 'UI';
|
||||
import { session as sessionRoute, liveSession as liveSessionRoute } from 'App/routes';
|
||||
|
||||
const PLAY_ICON_NAMES = {
|
||||
notPlayed: 'play-fill',
|
||||
played: 'play-circle-light',
|
||||
hovered: 'play-hover'
|
||||
}
|
||||
hovered: 'play-hover',
|
||||
};
|
||||
|
||||
const getDefaultIconName = (isViewed) => !isViewed ? PLAY_ICON_NAMES.notPlayed : PLAY_ICON_NAMES.played
|
||||
const getDefaultIconName = (isViewed: any) => (!isViewed ? PLAY_ICON_NAMES.notPlayed : PLAY_ICON_NAMES.played);
|
||||
|
||||
interface Props {
|
||||
isAssist: boolean;
|
||||
viewed: boolean;
|
||||
sessionId: string;
|
||||
onClick?: () => void;
|
||||
queryParams: any;
|
||||
}
|
||||
export default function PlayLink(props: Props) {
|
||||
const { isAssist, viewed, sessionId, onClick = null } = props
|
||||
const defaultIconName = getDefaultIconName(viewed)
|
||||
const { isAssist, viewed, sessionId, onClick = null, queryParams } = props;
|
||||
const defaultIconName = getDefaultIconName(viewed);
|
||||
|
||||
const [isHovered, toggleHover] = useState(false)
|
||||
const [iconName, setIconName] = useState(defaultIconName)
|
||||
const [isHovered, toggleHover] = useState(false);
|
||||
const [iconName, setIconName] = useState(defaultIconName);
|
||||
|
||||
useEffect(() => {
|
||||
if (isHovered) setIconName(PLAY_ICON_NAMES.hovered)
|
||||
else setIconName(getDefaultIconName(viewed))
|
||||
}, [isHovered, viewed])
|
||||
if (isHovered) setIconName(PLAY_ICON_NAMES.hovered);
|
||||
else setIconName(getDefaultIconName(viewed));
|
||||
}, [isHovered, viewed]);
|
||||
|
||||
return (
|
||||
<Link
|
||||
onClick={onClick ? onClick : () => {}}
|
||||
to={ isAssist ? liveSessionRoute(sessionId) : sessionRoute(sessionId) }
|
||||
to={isAssist ? liveSessionRoute(sessionId, queryParams) : sessionRoute(sessionId)}
|
||||
onMouseEnter={() => toggleHover(true)}
|
||||
onMouseLeave={() => toggleHover(false)}
|
||||
>
|
||||
<Icon name={iconName} size={38} color={isAssist ? "tealx" : "teal"} />
|
||||
<Icon name={iconName} size={38} color={isAssist ? 'tealx' : 'teal'} />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,192 +1,191 @@
|
|||
import React from 'react'
|
||||
import React, { useEffect } from 'react';
|
||||
import cn from 'classnames';
|
||||
import {
|
||||
CountryFlag,
|
||||
Avatar,
|
||||
TextEllipsis,
|
||||
Label,
|
||||
Icon,
|
||||
} from 'UI';
|
||||
import { CountryFlag, Avatar, TextEllipsis, Label, Icon } from 'UI';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { durationFormatted, formatTimeOrDate } from 'App/date';
|
||||
import stl from './sessionItem.module.css';
|
||||
import Counter from './Counter'
|
||||
import Counter from './Counter';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import SessionMetaList from './SessionMetaList';
|
||||
import PlayLink from './PlayLink';
|
||||
import ErrorBars from './ErrorBars';
|
||||
import { assist as assistRoute, liveSession, sessions as sessionsRoute, isRoute } from "App/routes";
|
||||
import { assist as assistRoute, liveSession, sessions as sessionsRoute, isRoute } from 'App/routes';
|
||||
import { capitalize } from 'App/utils';
|
||||
|
||||
const ASSIST_ROUTE = assistRoute();
|
||||
const ASSIST_LIVE_SESSION = liveSession()
|
||||
const ASSIST_LIVE_SESSION = liveSession();
|
||||
const SESSIONS_ROUTE = sessionsRoute();
|
||||
|
||||
interface Props {
|
||||
session: {
|
||||
sessionId: string;
|
||||
userBrowser: string;
|
||||
userOs: string;
|
||||
userId: string;
|
||||
userAnonymousId: string;
|
||||
userDisplayName: string;
|
||||
userCountry: string;
|
||||
startedAt: number;
|
||||
duration: string;
|
||||
eventsCount: number;
|
||||
errorsCount: number;
|
||||
pagesCount: number;
|
||||
viewed: boolean;
|
||||
favorite: boolean;
|
||||
userDeviceType: string;
|
||||
userUuid: string;
|
||||
userNumericHash: number;
|
||||
live: boolean;
|
||||
metadata: Record<string, any>;
|
||||
userSessionsCount: number;
|
||||
issueTypes: [];
|
||||
active: boolean;
|
||||
},
|
||||
onUserClick?: (userId: string, userAnonymousId: string) => void;
|
||||
hasUserFilter?: boolean;
|
||||
disableUser?: boolean;
|
||||
metaList?: Array<any>;
|
||||
// showActive?: boolean;
|
||||
lastPlayedSessionId?: string;
|
||||
live?: boolean;
|
||||
onClick?: any
|
||||
session: {
|
||||
sessionId: string;
|
||||
userBrowser: string;
|
||||
userOs: string;
|
||||
userId: string;
|
||||
userAnonymousId: string;
|
||||
userDisplayName: string;
|
||||
userCountry: string;
|
||||
startedAt: number;
|
||||
duration: string;
|
||||
eventsCount: number;
|
||||
errorsCount: number;
|
||||
pagesCount: number;
|
||||
viewed: boolean;
|
||||
favorite: boolean;
|
||||
userDeviceType: string;
|
||||
userUuid: string;
|
||||
userNumericHash: number;
|
||||
live: boolean;
|
||||
metadata: Record<string, any>;
|
||||
userSessionsCount: number;
|
||||
issueTypes: [];
|
||||
active: boolean;
|
||||
};
|
||||
onUserClick?: (userId: string, userAnonymousId: string) => void;
|
||||
hasUserFilter?: boolean;
|
||||
disableUser?: boolean;
|
||||
metaList?: Array<any>;
|
||||
// showActive?: boolean;
|
||||
lastPlayedSessionId?: string;
|
||||
live?: boolean;
|
||||
onClick?: any;
|
||||
}
|
||||
|
||||
function SessionItem(props: RouteComponentProps & Props) {
|
||||
const { settingsStore } = useStore();
|
||||
const { timezone } = settingsStore.sessionSettings;
|
||||
const { settingsStore } = useStore();
|
||||
const { timezone } = settingsStore.sessionSettings;
|
||||
const [isIframe, setIsIframe] = React.useState(false);
|
||||
|
||||
const {
|
||||
session,
|
||||
onUserClick = () => null,
|
||||
hasUserFilter = false,
|
||||
disableUser = false,
|
||||
metaList = [],
|
||||
lastPlayedSessionId,
|
||||
onClick = null,
|
||||
} = props;
|
||||
const {
|
||||
session,
|
||||
onUserClick = () => null,
|
||||
hasUserFilter = false,
|
||||
disableUser = false,
|
||||
metaList = [],
|
||||
lastPlayedSessionId,
|
||||
onClick = null,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
sessionId,
|
||||
userBrowser,
|
||||
userOs,
|
||||
userId,
|
||||
userAnonymousId,
|
||||
userDisplayName,
|
||||
userCountry,
|
||||
startedAt,
|
||||
duration,
|
||||
eventsCount,
|
||||
viewed,
|
||||
userDeviceType,
|
||||
userNumericHash,
|
||||
live,
|
||||
metadata,
|
||||
issueTypes,
|
||||
active,
|
||||
} = session;
|
||||
const {
|
||||
sessionId,
|
||||
userBrowser,
|
||||
userOs,
|
||||
userId,
|
||||
userAnonymousId,
|
||||
userDisplayName,
|
||||
userCountry,
|
||||
startedAt,
|
||||
duration,
|
||||
eventsCount,
|
||||
viewed,
|
||||
userDeviceType,
|
||||
userNumericHash,
|
||||
live,
|
||||
metadata,
|
||||
issueTypes,
|
||||
active,
|
||||
} = session;
|
||||
|
||||
const location = props.location;
|
||||
const location = props.location;
|
||||
const queryParams = Object.fromEntries(new URLSearchParams(location.search));
|
||||
|
||||
const formattedDuration = durationFormatted(duration);
|
||||
const hasUserId = userId || userAnonymousId;
|
||||
const isSessions = isRoute(SESSIONS_ROUTE, location.pathname);
|
||||
const isAssist = isRoute(ASSIST_ROUTE, location.pathname) || isRoute(ASSIST_LIVE_SESSION, location.pathname);
|
||||
const isLastPlayed = lastPlayedSessionId === sessionId;
|
||||
const formattedDuration = durationFormatted(duration);
|
||||
const hasUserId = userId || userAnonymousId;
|
||||
const isSessions = isRoute(SESSIONS_ROUTE, location.pathname);
|
||||
const isAssist = isRoute(ASSIST_ROUTE, location.pathname) || isRoute(ASSIST_LIVE_SESSION, location.pathname);
|
||||
const isLastPlayed = lastPlayedSessionId === sessionId;
|
||||
|
||||
const _metaList = Object.keys(metadata).filter(i => metaList.includes(i)).map(key => {
|
||||
const value = metadata[key];
|
||||
return { label: key, value };
|
||||
});
|
||||
const _metaList = Object.keys(metadata)
|
||||
.filter((i) => metaList.includes(i))
|
||||
.map((key) => {
|
||||
const value = metadata[key];
|
||||
return { label: key, value };
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={ cn(stl.sessionItem, "flex flex-col p-2") } id="session-item" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="flex items-start">
|
||||
<div className={ cn('flex items-center w-full')}>
|
||||
<div className="flex items-center pr-2 shrink-0" style={{ width: "40%"}}>
|
||||
<div><Avatar isActive={active} seed={ userNumericHash } isAssist={isAssist} /></div>
|
||||
<div className="flex flex-col overflow-hidden color-gray-medium ml-3 justify-between items-center shrink-0">
|
||||
<div
|
||||
className={cn('text-lg', {'color-teal cursor-pointer': !disableUser && hasUserId, [stl.userName]: !disableUser && hasUserId, 'color-gray-medium' : disableUser || !hasUserId})}
|
||||
onClick={() => (!disableUser && !hasUserFilter) && onUserClick(userId, userAnonymousId)}
|
||||
>
|
||||
<TextEllipsis text={userDisplayName} maxWidth="200px" popupProps={{ inverted: true, size: 'tiny' }} />
|
||||
return (
|
||||
<div className={cn(stl.sessionItem, 'flex flex-col p-2')} id="session-item" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="flex items-start">
|
||||
<div className={cn('flex items-center w-full')}>
|
||||
<div className="flex items-center pr-2 shrink-0" style={{ width: '40%' }}>
|
||||
<div>
|
||||
<Avatar isActive={active} seed={userNumericHash} isAssist={isAssist} />
|
||||
</div>
|
||||
<div className="flex flex-col overflow-hidden color-gray-medium ml-3 justify-between items-center shrink-0">
|
||||
<div
|
||||
className={cn('text-lg', {
|
||||
'color-teal cursor-pointer': !disableUser && hasUserId,
|
||||
[stl.userName]: !disableUser && hasUserId,
|
||||
'color-gray-medium': disableUser || !hasUserId,
|
||||
})}
|
||||
onClick={() => !disableUser && !hasUserFilter && onUserClick(userId, userAnonymousId)}
|
||||
>
|
||||
<TextEllipsis text={userDisplayName} maxWidth="200px" popupProps={{ inverted: true, size: 'tiny' }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ width: '20%' }} className="px-2 flex flex-col justify-between">
|
||||
<div>
|
||||
<TextEllipsis text={formatTimeOrDate(startedAt, timezone)} popupProps={{ inverted: true, size: 'tiny' }} />
|
||||
</div>
|
||||
<div className="flex items-center color-gray-medium py-1">
|
||||
{!isAssist && (
|
||||
<>
|
||||
<div className="color-gray-medium">
|
||||
<span className="mr-1">{eventsCount}</span>
|
||||
<span>{eventsCount === 0 || eventsCount > 1 ? 'Events' : 'Event'}</span>
|
||||
</div>
|
||||
<Icon name="circle-fill" size={3} className="mx-4" />
|
||||
</>
|
||||
)}
|
||||
<div>{live ? <Counter startTime={startedAt} /> : formattedDuration}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ width: '30%' }} className="px-2 flex flex-col justify-between">
|
||||
<div style={{ height: '21px' }}>
|
||||
<CountryFlag country={userCountry} style={{ paddingTop: '4px' }} label />
|
||||
</div>
|
||||
<div className="color-gray-medium flex items-center py-1">
|
||||
<span className="capitalize" style={{ maxWidth: '70px' }}>
|
||||
<TextEllipsis text={capitalize(userBrowser)} popupProps={{ inverted: true, size: 'tiny' }} />
|
||||
</span>
|
||||
<Icon name="circle-fill" size={3} className="mx-4" />
|
||||
<span className="capitalize" style={{ maxWidth: '70px' }}>
|
||||
<TextEllipsis text={capitalize(userOs)} popupProps={{ inverted: true, size: 'tiny' }} />
|
||||
</span>
|
||||
<Icon name="circle-fill" size={3} className="mx-4" />
|
||||
<span className="capitalize" style={{ maxWidth: '70px' }}>
|
||||
<TextEllipsis text={capitalize(userDeviceType)} popupProps={{ inverted: true, size: 'tiny' }} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{isSessions && (
|
||||
<div style={{ width: '10%' }} className="self-center px-2 flex items-center">
|
||||
<ErrorBars count={issueTypes.length} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ width: "20%" }} className="px-2 flex flex-col justify-between">
|
||||
<div>
|
||||
<TextEllipsis text={formatTimeOrDate(startedAt, timezone)} popupProps={{ inverted: true, size: 'tiny' }} />
|
||||
</div>
|
||||
<div className="flex items-center color-gray-medium py-1">
|
||||
{!isAssist && (
|
||||
<>
|
||||
<div className="color-gray-medium">
|
||||
<span className="mr-1">{ eventsCount }</span>
|
||||
<span>{ eventsCount === 0 || eventsCount > 1 ? 'Events' : 'Event' }</span>
|
||||
</div>
|
||||
<Icon name="circle-fill" size={3} className="mx-4" />
|
||||
</>
|
||||
)}
|
||||
<div>{ live ? <Counter startTime={startedAt} /> : formattedDuration }</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ width: "30%" }} className="px-2 flex flex-col justify-between">
|
||||
<div style={{ height: '21px'}}>
|
||||
<CountryFlag country={ userCountry } style={{ paddingTop: '4px' }} label />
|
||||
</div>
|
||||
<div className="color-gray-medium flex items-center py-1">
|
||||
<span className="capitalize" style={{ maxWidth: '70px'}}>
|
||||
<TextEllipsis text={ capitalize(userBrowser) } popupProps={{ inverted: true, size: "tiny" }} />
|
||||
</span>
|
||||
<Icon name="circle-fill" size={3} className="mx-4" />
|
||||
<span className="capitalize" style={{ maxWidth: '70px'}}>
|
||||
<TextEllipsis text={ capitalize(userOs) } popupProps={{ inverted: true, size: "tiny" }} />
|
||||
</span>
|
||||
<Icon name="circle-fill" size={3} className="mx-4" />
|
||||
<span className="capitalize" style={{ maxWidth: '70px'}}>
|
||||
<TextEllipsis text={ capitalize(userDeviceType) } popupProps={{ inverted: true, size: "tiny" }} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{ isSessions && (
|
||||
<div style={{ width: "10%"}} className="self-center px-2 flex items-center">
|
||||
<ErrorBars count={issueTypes.length} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<div className={ stl.playLink } id="play-button" data-viewed={ viewed }>
|
||||
{ isSessions && (
|
||||
<div className="mr-4 flex-shrink-0 w-24">
|
||||
{ isLastPlayed && (
|
||||
<Label className="bg-gray-lightest p-1 px-2 rounded-lg">
|
||||
<span className="color-gray-medium text-xs" style={{ whiteSpace: 'nowrap'}}>LAST PLAYED</span>
|
||||
</Label>
|
||||
)}
|
||||
<div className="flex items-center">
|
||||
<div className={stl.playLink} id="play-button" data-viewed={viewed}>
|
||||
{isSessions && (
|
||||
<div className="mr-4 flex-shrink-0 w-24">
|
||||
{isLastPlayed && (
|
||||
<Label className="bg-gray-lightest p-1 px-2 rounded-lg">
|
||||
<span className="color-gray-medium text-xs" style={{ whiteSpace: 'nowrap' }}>
|
||||
LAST PLAYED
|
||||
</span>
|
||||
</Label>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<PlayLink isAssist={isAssist} sessionId={sessionId} viewed={viewed} onClick={onClick} queryParams={queryParams} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<PlayLink
|
||||
isAssist={isAssist}
|
||||
sessionId={sessionId}
|
||||
viewed={viewed}
|
||||
onClick={onClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{_metaList.length > 0 && <SessionMetaList className="mt-4" metaList={_metaList} />}
|
||||
</div>
|
||||
{ _metaList.length > 0 && (
|
||||
<SessionMetaList className="mt-4" metaList={_metaList} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default withRouter<Props>(observer<Props>(SessionItem))
|
||||
export default withRouter<Props>(observer<Props>(SessionItem));
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ export const sessions = params => queried('/sessions', params);
|
|||
export const assist = params => queried('/assist', params);
|
||||
|
||||
export const session = (sessionId = ':sessionId', hash) => hashed(`/session/${ sessionId }`, hash);
|
||||
export const liveSession = (sessionId = ':sessionId', hash) => hashed(`/assist/${ sessionId }`, hash);
|
||||
export const liveSession = (sessionId = ':sessionId', params, hash) => hashed(queried(`/assist/${ sessionId }`, params), hash);
|
||||
// export const liveSession = (sessionId = ':sessionId', hash) => hashed(`/live/session/${ sessionId }`, hash);
|
||||
|
||||
export const errors = params => queried('/errors', params);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue