openreplay/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx
Delirium 1326bb2eae
feat spot: init commit for extension (#2452)
* feat spot: init commit for extension

* nvmrc

* fix login flow

* Spots Gridview Updates (#2422)

* feat ui: login flow for spot extension

* spot list, store and service created

* some fixing for header

* start work on single spot

* spot player start

* header for player, comments, icons, etc

* split stuff into compoennts, create player state manager

* player controls, activity panel etc etc

* comments, empty page, rename and stuff

* interval buttons etc

* access modal

* pubkey support

* fix tooltip

* limit 10 -> 9

* hls lib instead of videojs

* some warnings

* fix date display for exp

* change public links

* display more client data

* fix cleaning, init comment

* map network to replay player network ev

* stream support, console panel, close panels on X

* fixing streaming, destroy on leave

* fix autoplay

* show notification on spot login

* fix spot login

* backup player added, fix audio issue

* show thumbnail when no video, add spot roles

* add poster thumbnail

* some fixes to video check

* fix events jump

* fix play btn

* try catch over pubkey

* feat ui: login flow for spot extension

* spot list, store and service created

* some fixing for header

* start work on single spot

* spot player start

* header for player, comments, icons, etc

* split stuff into compoennts, create player state manager

* player controls, activity panel etc etc

* comments, empty page, rename and stuff

* interval buttons etc

* access modal

* pubkey support

* fix tooltip

* limit 10 -> 9

* hls lib instead of videojs

* some warnings

* fix date display for exp

* change public links

* display more client data

* fix cleaning, init comment

* map network to replay player network ev

* stream support, console panel, close panels on X

* fixing streaming, destroy on leave

* fix autoplay

* show notification on spot login

* fix spot login

* backup player added, fix audio issue

* show thumbnail when no video, add spot roles

* add poster thumbnail

* some fixes to video check

* fix events jump

* fix play btn

* try catch over pubkey

* icons

* Various updates

* Update SVG.tsx

* Update SideMenu.tsx

* SpotList & Menu updates

* feat ui: login flow for spot extension

* spot list, store and service created

* some fixing for header

* start work on single spot

* spot player start

* header for player, comments, icons, etc

* split stuff into compoennts, create player state manager

* player controls, activity panel etc etc

* comments, empty page, rename and stuff

* interval buttons etc

* access modal

* pubkey support

* fix tooltip

* limit 10 -> 9

* hls lib instead of videojs

* some warnings

* fix date display for exp

* change public links

* display more client data

* fix cleaning, init comment

* map network to replay player network ev

* stream support, console panel, close panels on X

* fixing streaming, destroy on leave

* fix autoplay

* show notification on spot login

* fix spot login

* backup player added, fix audio issue

* show thumbnail when no video, add spot roles

* add poster thumbnail

* some fixes to video check

* fix events jump

* fix play btn

* try catch over pubkey

* icons

* spot login pinging

* Spot List & Player Updates

* move spot login flow to login comp, use separate spot login path for unique jwt

* invalidate spot jwt on logout

* add visual data on page load event

* typo fix

* Spot Listing improvements post review.

* Update SpotListItem.tsx

* Improved Spot List and Item Details

* Minor improvements

* More improvements

* Public player header improvements

* Moved formatExpirationTime to utils

* fixes after merge

---------

Co-authored-by: nick-delirium <nikita@openreplay.com>

* set sso link to <a>?

* some small perf fixes

* login duck reformat...

* Update frontend.yaml

* add observer to spot list header

* split list header

* update spotjwt param in router

* fix toast in router

* fix async fetch, move ctx

* capture space btn ev

* fix header link

* public sharing error msg

* fix err msg for unsuccessful rec start

* fix list alignment

* Caching assets. Finally!!!

* fix typing in comment field

* add pubkey to comments, fix console jump btn

* no content comp

* change refresh token logic

* move thumbnail ts

* move thumbnail ts

* fix tab change

* switch up toggler

* early exit if no jwt present

* regenerate icons

* fix location str

* fix ctx

* change thumnail res, return autoplay for video player

* parse links in console rows, fix injected method parse?

* remove ts from js

* fix console parsing order?

* fixes for autoplay

* xray for spot player

* move to spot list after login;
esc to cancel;
fix signup link;
move ux commit

* kb sc for skipping; xray for spot ext

* track aborted requests

* tooltip for readability

* fixing empty state

* New blank state + various minor improvements (#2471)

* New blank state + various minor improvements

* apres merge

---------

Co-authored-by: nick-delirium <nikita@openreplay.com>

* rm temp v

* init or card

* empty state debug

* empty state debug

* empty state debug

* fix initor img

* spotonly scope support

* Improved Spot dead-end pages (#2475)

* Improved Spot dead-end pages

* Initiate OpenReplay Setup and some more

* get scope changes

* fix crash

* scope upgrade/downgrade

* scope setup flow

* ping for backend

* upgrade wxt deps

* cancel ping int on expiration

* check rec status

* fix ping

* check video processing state

* check video processing state

* fix xray close, network highlight, fcp rounding

* update wxt, move open spot stuff to settings

* fix some history issues

* fix spot login flow

* fix spot login again

* fix spot login again

* don't send two requests

* limit messages for logged users

* limit messages for logged users

* fix public ignore

* microphone stuff

* microphone stuff

* Various improvements (#2509)

* Various improvements

- Updated icons in mic settings
- Included prefix in Spot title
- Save recording notification has been updated
- Other minor UI improvements

* Inline declaration of spot name field, and settings UI

* str f

---------

Co-authored-by: nick-delirium <nikita@openreplay.com>

* UI changes in player header, spot list (#2510)

* Added UI elements in player page

- Badge with counts for comments
- Download and Delete dropdown in player
- Spot selection -- UI improvement

* Minor copy updates

* completing changes

---------

Co-authored-by: nick-delirium <nikita@openreplay.com>

* rm cmt

* fix cellmeasurer

* thumbnail dur

* fix download

* Minor fixes (#2512)

- Spot delete confirmation
- Spot comments UI update
- Minor copy updates

* limit number of notif messages

* add spot title to doc title, add cache groups for webpack

* drop mic controls from recording popup view

* fix for webpack compress

* fix for auto mic pickup

* change status banners

* move svgs around, remove undefined check

* refactor svgs

* fix timetable scaling

* fix error popup

* self contain css

* pre-select spot on spot onboarding

---------

Co-authored-by: Sudheer Salavadi <connect.uxmaster@gmail.com>
Co-authored-by: Rajesh Rajendran <rjshrjndrn@users.noreply.github.com>
2024-08-29 13:35:58 +02:00

412 lines
13 KiB
TypeScript

import { Segmented } from 'antd';
import cn from 'classnames';
import { observer } from 'mobx-react-lite';
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import {
MobilePlayerContext,
PlayerContext,
} from 'App/components/Session/playerContext';
import { useStore } from 'App/mstore';
import SummaryBlock from 'Components/Session/Player/ReplayPlayer/SummaryBlock';
import { SummaryButton } from 'Components/Session_/Player/Controls/Controls';
import TimelineZoomButton from 'Components/Session_/Player/Controls/components/TimelineZoomButton';
import { setZoomTab, toggleBottomBlock } from 'Duck/components/player';
import { Icon, NoContent } from 'UI';
import BottomBlock from '../BottomBlock';
import EventRow from './components/EventRow';
import FeatureSelection, {
HELP_MESSAGE,
} from './components/FeatureSelection/FeatureSelection';
import OverviewPanelContainer from './components/OverviewPanelContainer';
import TimelinePointer from './components/TimelinePointer';
import TimelineScale from './components/TimelineScale';
import VerticalPointerLine, { VerticalPointerLineComp } from './components/VerticalPointerLine';
function MobileOverviewPanelCont({
issuesList,
sessionId,
zoomEnabled,
zoomStartTs,
zoomEndTs,
setZoomTab,
zoomTab,
}: {
issuesList: Record<string, any>[];
sessionId: string;
zoomEnabled: boolean;
zoomStartTs: number;
zoomEndTs: number;
setZoomTab: (tab: string) => void;
zoomTab: 'overview' | 'journey' | 'issues' | 'errors';
}) {
const { aiSummaryStore } = useStore();
const { store, player } = React.useContext(MobilePlayerContext);
const [dataLoaded, setDataLoaded] = React.useState(false);
const [selectedFeatures, setSelectedFeatures] = React.useState([
'PERFORMANCE',
'FRUSTRATIONS',
'ERRORS',
'NETWORK',
]);
const {
endTime,
eventList: eventsList,
frustrationsList,
exceptionsList,
fetchList,
performanceChartData,
performanceList,
} = store.get();
const fetchPresented = fetchList.length > 0;
const checkInZoomRange = (list: any[]) => {
return list.filter((i) =>
zoomEnabled ? i.time >= zoomStartTs && i.time <= zoomEndTs : true
);
};
const resources = {
NETWORK: checkInZoomRange(
fetchList.filter((r: any) => r.status >= 400 || r.isRed || r.isYellow)
),
ERRORS: checkInZoomRange(exceptionsList),
EVENTS: checkInZoomRange(eventsList),
PERFORMANCE: checkInZoomRange(performanceChartData),
FRUSTRATIONS: checkInZoomRange(frustrationsList),
};
useEffect(() => {
if (dataLoaded) {
return;
}
if (
exceptionsList.length > 0 ||
eventsList.length > 0 ||
issuesList.length > 0 ||
performanceChartData.length > 0 ||
frustrationsList.length > 0
) {
setDataLoaded(true);
}
}, [
issuesList,
exceptionsList,
eventsList,
performanceChartData,
frustrationsList,
]);
React.useEffect(() => {
player.scale();
}, [selectedFeatures]);
const originStr = window.env.ORIGIN || window.location.origin;
const isSaas = /app\.openreplay\.com/.test(originStr);
return (
<PanelComponent
resources={resources}
endTime={endTime}
selectedFeatures={selectedFeatures}
fetchPresented={fetchPresented}
setSelectedFeatures={setSelectedFeatures}
isMobile
performanceList={performanceList}
sessionId={sessionId}
showSummary={isSaas}
toggleSummary={() =>
aiSummaryStore.setToggleSummary(!aiSummaryStore.toggleSummary)
}
summaryChecked={aiSummaryStore.toggleSummary}
setZoomTab={setZoomTab}
zoomTab={zoomTab}
/>
);
}
function WebOverviewPanelCont({
sessionId,
zoomEnabled,
zoomStartTs,
zoomEndTs,
setZoomTab,
zoomTab,
}: {
sessionId: string;
zoomEnabled: boolean;
zoomStartTs: number;
zoomEndTs: number;
setZoomTab: (tab: string) => void;
zoomTab: 'overview' | 'journey' | 'issues' | 'errors';
}) {
const { aiSummaryStore } = useStore();
const { store } = React.useContext(PlayerContext);
const [selectedFeatures, setSelectedFeatures] = React.useState([
'PERFORMANCE',
'FRUSTRATIONS',
'ERRORS',
'NETWORK',
]);
const { endTime, currentTab, tabStates } = store.get();
const stackEventList = tabStates[currentTab]?.stackList || [];
const frustrationsList = tabStates[currentTab]?.frustrationsList || [];
const exceptionsList = tabStates[currentTab]?.exceptionsList || [];
const resourceListUnmap = tabStates[currentTab]?.resourceList || [];
const fetchList = tabStates[currentTab]?.fetchList || [];
const graphqlList = tabStates[currentTab]?.graphqlList || [];
const performanceChartData =
tabStates[currentTab]?.performanceChartData || [];
const fetchPresented = fetchList.length > 0;
const resourceList = resourceListUnmap
.filter((r: any) => r.isRed || r.isYellow)
// @ts-ignore
.concat(fetchList.filter((i: any) => parseInt(i.status) >= 400))
// @ts-ignore
.concat(graphqlList.filter((i: any) => parseInt(i.status) >= 400))
.filter((i: any) => i.type === 'fetch');
const checkInZoomRange = (list: any[]) => {
return list.filter((i) =>
zoomEnabled ? i.time >= zoomStartTs && i.time <= zoomEndTs : true
);
};
const resources: any = React.useMemo(() => {
return {
NETWORK: checkInZoomRange(resourceList),
ERRORS: checkInZoomRange(exceptionsList),
EVENTS: checkInZoomRange(stackEventList),
PERFORMANCE: checkInZoomRange(performanceChartData),
FRUSTRATIONS: checkInZoomRange(frustrationsList),
};
}, [tabStates, currentTab, zoomEnabled, zoomStartTs, zoomEndTs]);
const originStr = window.env.ORIGIN || window.location.origin;
const isSaas = /app\.openreplay\.com/.test(originStr);
return (
<PanelComponent
resources={resources}
endTime={endTime}
selectedFeatures={selectedFeatures}
fetchPresented={fetchPresented}
setSelectedFeatures={setSelectedFeatures}
showSummary={isSaas}
toggleSummary={() =>
aiSummaryStore.setToggleSummary(!aiSummaryStore.toggleSummary)
}
summaryChecked={aiSummaryStore.toggleSummary}
sessionId={sessionId}
setZoomTab={setZoomTab}
zoomTab={zoomTab}
/>
);
}
export function SpotOverviewPanelCont({
resourceList,
exceptionsList,
spotTime,
spotEndTime,
onClose,
}: any) {
const selectedFeatures = ['ERRORS', 'NETWORK'];
const fetchPresented = false; // TODO
const endTime = 0; // TODO
const resources = {
NETWORK: resourceList,
ERRORS: exceptionsList,
};
return (
<PanelComponent
resources={resources}
endTime={endTime}
selectedFeatures={selectedFeatures}
fetchPresented={fetchPresented}
isSpot
spotTime={spotTime}
spotEndTime={spotEndTime}
onClose={onClose}
/>
);
}
function PanelComponent({
selectedFeatures,
endTime,
resources,
fetchPresented,
setSelectedFeatures,
isMobile,
performanceList,
showSummary,
toggleSummary,
summaryChecked,
sessionId,
zoomTab,
setZoomTab,
isSpot,
spotTime,
spotEndTime,
onClose,
}: any) {
return (
<React.Fragment>
<BottomBlock style={{ height: '100%' }}>
<BottomBlock.Header customClose={onClose}>
<div className="mr-4 flex items-center gap-2">
<span className={'font-semibold text-black'}>X-Ray</span>
{showSummary ? (
<>
<SummaryButton
withToggle
onClick={toggleSummary}
toggleValue={summaryChecked}
/>
{summaryChecked ? (
<Segmented
value={zoomTab}
onChange={(val) => setZoomTab(val)}
options={[
{
label: 'Overview',
value: 'overview',
},
{
label: 'User journey',
value: 'journey',
},
{
label: 'Issues',
value: 'issues',
},
{
label: 'Suggestions',
value: 'errors',
},
]}
/>
) : null}
</>
) : null}
</div>
{isSpot ? null : (
<div className="flex items-center h-20 mr-4 gap-2">
<TimelineZoomButton />
<FeatureSelection
list={selectedFeatures}
updateList={setSelectedFeatures}
/>
</div>
)}
</BottomBlock.Header>
<BottomBlock.Content className={'overflow-y-auto'}>
{summaryChecked ? <SummaryBlock sessionId={sessionId} /> : null}
<OverviewPanelContainer endTime={endTime}>
<TimelineScale endTime={endTime} />
<div
style={{ width: 'calc(100% - 1rem)', margin: '0 auto' }}
className="transition relative"
>
<NoContent
show={selectedFeatures.length === 0}
style={{ height: '60px', minHeight: 'unset', padding: 0 }}
title={
<div className="flex items-center">
<Icon name="info-circle" className="mr-2" size="18" />
Select a debug option to visualize on timeline.
</div>
}
>
{isSpot ? <VerticalPointerLineComp time={spotTime} endTime={spotEndTime} /> : <VerticalPointerLine />}
{selectedFeatures.map((feature: any, index: number) => (
<div
key={feature}
className={cn('border-b last:border-none relative', {
'bg-white': index % 2,
})}
>
<EventRow
isGraph={feature === 'PERFORMANCE'}
title={feature}
list={resources[feature]}
renderElement={(pointer: any) => (
<TimelinePointer
pointer={pointer}
type={feature}
fetchPresented={fetchPresented}
/>
)}
endTime={isSpot ? spotEndTime : endTime}
message={HELP_MESSAGE[feature]}
/>
{isMobile && feature === 'PERFORMANCE' ? (
<div
className={
'absolute top-0 left-0 flex items-center py-4 w-full'
}
>
<EventRow
isGraph={false}
title={''}
list={performanceList}
renderElement={(pointer: any) => (
<div className="rounded bg-white p-1 border">
<TimelinePointer
pointer={pointer}
type={'FRUSTRATIONS'}
fetchPresented={fetchPresented}
/>
</div>
)}
endTime={endTime}
/>
</div>
) : null}
</div>
))}
</NoContent>
</div>
</OverviewPanelContainer>
</BottomBlock.Content>
</BottomBlock>
</React.Fragment>
);
}
export const OverviewPanel = connect(
(state: Record<string, any>) => ({
issuesList: state.getIn(['sessions', 'current']).issues,
sessionId: state.getIn(['sessions', 'current']).sessionId,
zoomEnabled: state.getIn(['components', 'player']).timelineZoom.enabled,
zoomStartTs: state.getIn(['components', 'player']).timelineZoom.startTs,
zoomEndTs: state.getIn(['components', 'player']).timelineZoom.endTs,
}),
{
toggleBottomBlock,
setZoomTab,
}
)(observer(WebOverviewPanelCont));
export const MobileOverviewPanel = connect(
(state: Record<string, any>) => ({
issuesList: state.getIn(['sessions', 'current']).issues,
sessionId: state.getIn(['sessions', 'current']).sessionId,
zoomEnabled: state.getIn(['components', 'player']).timelineZoom.enabled,
zoomStartTs: state.getIn(['components', 'player']).timelineZoom.startTs,
zoomEndTs: state.getIn(['components', 'player']).timelineZoom.endTs,
zoomTab: state.getIn(['components', 'player']).zoomTab,
}),
{
toggleBottomBlock,
setZoomTab,
}
)(observer(MobileOverviewPanelCont));