updating foss with saas changes
This commit is contained in:
parent
0f48518b51
commit
084da23442
14 changed files with 92 additions and 181 deletions
|
|
@ -22,6 +22,7 @@ function Player() {
|
|||
<div className={cn('relative flex-1', 'overflow-visible')}>
|
||||
<Overlay isClickmap />
|
||||
<div
|
||||
data-openreplay-obscured
|
||||
className={cn(stl.screenWrapper, stl.checkers, '!overflow-y-scroll')}
|
||||
style={{ maxHeight: 800 }}
|
||||
ref={screenWrapper}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import styles from 'Components/Session_/playerBlock.module.css';
|
|||
import ClipPlayerOverlay from 'Components/Session/Player/ClipPlayer/ClipPlayerOverlay';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Icon } from 'UI';
|
||||
import { Empty } from 'antd';
|
||||
|
||||
interface Props {
|
||||
session: Session;
|
||||
|
|
@ -25,8 +26,8 @@ interface Props {
|
|||
function ClipPlayerContent(props: Props) {
|
||||
const playerContext = React.useContext<IPlayerContext>(PlayerContext);
|
||||
const screenWrapper = React.useRef<HTMLDivElement>(null);
|
||||
const { time } = playerContext.store.get();
|
||||
const { range, isFull } = props;
|
||||
const { time, error } = playerContext.store.get();
|
||||
const { range, isFull, isHighlight } = props;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!playerContext.player) return;
|
||||
|
|
@ -57,7 +58,16 @@ function ClipPlayerContent(props: Props) {
|
|||
|
||||
const outerHeight = props.isHighlight ? 556 + 39 : 556;
|
||||
const innerHeight = props.isHighlight ? 504 + 39 : 504;
|
||||
return (
|
||||
return error ? (
|
||||
<div
|
||||
className="inset-0 flex items-center justify-center absolute"
|
||||
style={{ height: 'auto' }}
|
||||
>
|
||||
<div className="flex flex-col items-center">
|
||||
<Empty description="Session not found." />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={cn(
|
||||
styles.playerBlock,
|
||||
|
|
@ -73,7 +83,10 @@ function ClipPlayerContent(props: Props) {
|
|||
>
|
||||
<div className={cn(stl.playerBody, 'flex flex-1 flex-col relative')}>
|
||||
<div className="relative flex-1 overflow-hidden group">
|
||||
<ClipPlayerOverlay autoplay={props.autoplay} />
|
||||
<ClipPlayerOverlay
|
||||
isHighlight={isHighlight}
|
||||
autoplay={props.autoplay}
|
||||
/>
|
||||
<div
|
||||
className={cn(stl.screenWrapper, stl.checkers)}
|
||||
ref={screenWrapper}
|
||||
|
|
@ -91,7 +104,11 @@ function ClipPlayerContent(props: Props) {
|
|||
<div className="leading-none font-medium">{props.message}</div>
|
||||
</div>
|
||||
) : null}
|
||||
<ClipPlayerControls isFull={isFull} session={props.session} range={props.range} />
|
||||
<ClipPlayerControls
|
||||
isFull={isFull}
|
||||
session={props.session}
|
||||
range={props.range}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ function ClipPlayerHeader(props: Props) {
|
|||
return (
|
||||
<div className="bg-white p-3 flex justify-between items-center border-b relative">
|
||||
{isHighlight && !isFull ? <PartialSessionBadge /> : null}
|
||||
<UserCard session={props.session} />
|
||||
{props.session ? <UserCard session={props.session} /> : null}
|
||||
|
||||
<Space>
|
||||
<Tooltip title={t('Copy link to clipboard')} placement="bottom">
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ import AutoplayTimer from 'Components/Session/Player/ClipPlayer/AutoPlayTimer';
|
|||
|
||||
interface Props {
|
||||
autoplay: boolean;
|
||||
isHighlight?: boolean;
|
||||
}
|
||||
|
||||
function Overlay({ autoplay }: Props) {
|
||||
function Overlay({ autoplay, isHighlight }: Props) {
|
||||
const { player, store } = React.useContext(PlayerContext);
|
||||
const togglePlay = () => player.togglePlay();
|
||||
|
||||
|
|
@ -19,9 +20,11 @@ function Overlay({ autoplay }: Props) {
|
|||
return (
|
||||
<>
|
||||
{messagesLoading ? <Loader /> : null}
|
||||
{/* <div className="hidden group-hover:block"> */}
|
||||
{/* <ClipFeedback/> */}
|
||||
{/* </div> */}
|
||||
{isHighlight ? null : (
|
||||
<div className="hidden group-hover:block">
|
||||
<ClipFeedback />
|
||||
</div>
|
||||
)}
|
||||
<PlayIconLayer playing={playing} togglePlay={togglePlay} />
|
||||
{completed && autoplay && <AutoplayTimer />}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ interface Props {}
|
|||
|
||||
function QueueControls(props: Props) {
|
||||
const { t } = useTranslation();
|
||||
const { clipStore, projectsStore, sessionStore, searchStore } = useStore();
|
||||
const { clipStore } = useStore();
|
||||
const previousId = clipStore.prevId;
|
||||
const { nextId } = clipStore;
|
||||
|
||||
|
|
|
|||
|
|
@ -132,17 +132,7 @@ function Timeline({ range }: any) {
|
|||
// };
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center w-full"
|
||||
style={
|
||||
{
|
||||
// top: '-4px',
|
||||
// zIndex: 100,
|
||||
// maxWidth: 'calc(100% - 5rem)',
|
||||
// left: '3.5rem',
|
||||
}
|
||||
}
|
||||
>
|
||||
<div className="flex items-center w-full">
|
||||
<div
|
||||
className={stl.progress}
|
||||
onClick={ready ? jumpToTime : undefined}
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ function Player(props: IProps) {
|
|||
isInspMode ? stl.solidBg : stl.checkers,
|
||||
)}
|
||||
ref={screenWrapper}
|
||||
data-openreplay-obscured
|
||||
/>
|
||||
</div>
|
||||
{!fullscreen && !!bottomBlock && (
|
||||
|
|
|
|||
|
|
@ -7,15 +7,10 @@ import { debounce } from 'App/utils';
|
|||
import { IResourceRequest, IResourceTiming } from 'App/player';
|
||||
import { WsChannel } from 'App/player/web/messages';
|
||||
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||
import MDRenderer from 'Shared/MDRenderer/MDRenderer';
|
||||
|
||||
let debounceUpdate: any = () => {};
|
||||
|
||||
const boldLine = /\*\*(.*?)\*\*/i;
|
||||
|
||||
function isTitleLine(line: string): boolean {
|
||||
return boldLine.test(line);
|
||||
}
|
||||
|
||||
function SummaryBlock({ sessionId }: { sessionId: string }) {
|
||||
const { store } = React.useContext(PlayerContext);
|
||||
const { tabStates } = store.get();
|
||||
|
|
@ -24,7 +19,7 @@ function SummaryBlock({ sessionId }: { sessionId: string }) {
|
|||
const zoomEnabled = uiPlayerStore.timelineZoom.enabled;
|
||||
const zoomStartTs = uiPlayerStore.timelineZoom.startTs;
|
||||
const zoomEndTs = uiPlayerStore.timelineZoom.endTs;
|
||||
const { zoomTab } = uiPlayerStore;
|
||||
const zoomTab = uiPlayerStore.zoomTab;
|
||||
|
||||
React.useEffect(() => {
|
||||
debounceUpdate = debounce(
|
||||
|
|
@ -76,31 +71,13 @@ function SummaryBlock({ sessionId }: { sessionId: string }) {
|
|||
}
|
||||
}, [zoomTab]);
|
||||
|
||||
const formattedText = aiSummaryStore.text.split('\n').map((line) => {
|
||||
if (isTitleLine(line)) {
|
||||
return (
|
||||
<div className="font-semibold mt-2">{line.replace(/\*/g, '')}</div>
|
||||
);
|
||||
}
|
||||
if (line.startsWith('*')) {
|
||||
return (
|
||||
<li className="ml-1 marker:mr-1 flex items-center gap-1">
|
||||
<CodeStringFormatter text={line.replace(/\*/g, '')} />
|
||||
</li>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
<CodeStringFormatter text={line} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={summaryBlockStyle}>
|
||||
{aiSummaryStore.text ? (
|
||||
<div className="rounded p-4 bg-white whitespace-pre-wrap flex flex-col">
|
||||
{formattedText.map((v) => v)}
|
||||
<div
|
||||
className={'rounded p-4 bg-white whitespace-pre-wrap flex flex-col'}
|
||||
>
|
||||
<MDRenderer content={aiSummaryStore.text} />
|
||||
</div>
|
||||
) : (
|
||||
<TextPlaceholder />
|
||||
|
|
@ -111,44 +88,31 @@ function SummaryBlock({ sessionId }: { sessionId: string }) {
|
|||
|
||||
function TextPlaceholder() {
|
||||
return (
|
||||
<div className="animate-pulse rounded p-4 bg-white whitespace-pre-wrap flex flex-col gap-2">
|
||||
<div className="h-2 bg-gray-medium rounded" />
|
||||
<div className="h-2 bg-gray-medium rounded" />
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<div className="h-2 bg-gray-medium rounded col-span-2" />
|
||||
<div className="h-2 bg-gray-medium rounded col-span-1" />
|
||||
<div
|
||||
className={
|
||||
'animate-pulse rounded p-4 bg-white whitespace-pre-wrap flex flex-col gap-2'
|
||||
}
|
||||
>
|
||||
<div className={'h-2 bg-gray-medium rounded'} />
|
||||
<div className={'h-2 bg-gray-medium rounded'} />
|
||||
<div className={'grid grid-cols-3 gap-2'}>
|
||||
<div className={'h-2 bg-gray-medium rounded col-span-2'} />
|
||||
<div className={'h-2 bg-gray-medium rounded col-span-1'} />
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-4 gap-2 mt-3">
|
||||
<div className="h-2 bg-gray-medium rounded col-span-1" />
|
||||
<div className="h-2 bg-gray-medium rounded col-span-1" />
|
||||
<div className="h-2 bg-gray-medium rounded col-span-2" />
|
||||
<div className={'grid grid-cols-4 gap-2 mt-3'}>
|
||||
<div className={'h-2 bg-gray-medium rounded col-span-1'} />
|
||||
<div className={'h-2 bg-gray-medium rounded col-span-1'} />
|
||||
<div className={'h-2 bg-gray-medium rounded col-span-2'} />
|
||||
</div>
|
||||
<div className="grid grid-cols-4 gap-2">
|
||||
<div className="h-2 bg-gray-medium rounded col-span-2" />
|
||||
<div className="h-2 bg-transparent rounded col-span-2" />
|
||||
<div className={'grid grid-cols-4 gap-2'}>
|
||||
<div className={'h-2 bg-gray-medium rounded col-span-2'} />
|
||||
<div className={'h-2 bg-transparent rounded col-span-2'} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CodeStringFormatter({ text }: { text: string }) {
|
||||
const parts = text.split(/(`[^`]*`)/).map((part, index) =>
|
||||
part.startsWith('`') && part.endsWith('`') ? (
|
||||
<div
|
||||
key={index}
|
||||
className="whitespace-nowrap bg-gray-lightest font-mono mx-1 px-1 border"
|
||||
>
|
||||
{part.substring(1, part.length - 1)}
|
||||
</div>
|
||||
) : (
|
||||
<span key={index}>{part}</span>
|
||||
),
|
||||
);
|
||||
|
||||
return <>{parts}</>;
|
||||
}
|
||||
|
||||
const summaryBlockStyle: React.CSSProperties = {
|
||||
background:
|
||||
'linear-gradient(180deg, #E8EBFF -24.14%, rgba(236, 254, 255, 0.00) 100%)',
|
||||
|
|
|
|||
4
frontend/app/components/shared/MDRenderer/MDRenderer.tsx
Normal file
4
frontend/app/components/shared/MDRenderer/MDRenderer.tsx
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export default function MDRenderer(props: any) {
|
||||
console.warn('saas comp')
|
||||
return null;
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ const { Text } = Typography;
|
|||
function ProjectDropdown(props: { location: any }) {
|
||||
const mstore = useStore();
|
||||
const { t } = useTranslation();
|
||||
const { projectsStore, searchStore, searchStoreLive, userStore } = mstore;
|
||||
const { projectsStore, searchStore, searchStoreLive, userStore, aiFiltersStore } = mstore;
|
||||
const { account } = userStore;
|
||||
const sites = projectsStore.list;
|
||||
const { siteId } = projectsStore;
|
||||
|
|
@ -32,6 +32,7 @@ function ProjectDropdown(props: { location: any }) {
|
|||
|
||||
const handleSiteChange = async (newSiteId: string) => {
|
||||
mstore.initClient();
|
||||
aiFiltersStore.clearFilters();
|
||||
setSiteId(newSiteId);
|
||||
searchStore.clearSearch();
|
||||
searchStore.clearList();
|
||||
|
|
|
|||
|
|
@ -1,100 +1,5 @@
|
|||
import { CloseOutlined, EnterOutlined } from '@ant-design/icons';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useState } from 'react';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { Input } from 'UI';
|
||||
|
||||
const AiSearchField = observer(() => {
|
||||
const { searchStore } = useStore();
|
||||
const appliedFilter = searchStore.instance;
|
||||
const hasFilters =
|
||||
appliedFilter && appliedFilter.filters && appliedFilter.filters.length > 0;
|
||||
const { aiFiltersStore } = useStore();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const onSearchChange = ({ target: { value } }: any) => {
|
||||
setSearchQuery(value);
|
||||
};
|
||||
|
||||
const fetchResults = () => {
|
||||
if (searchQuery) {
|
||||
void aiFiltersStore.getSearchFilters(searchQuery);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: any) => {
|
||||
if (event.key === 'Enter') {
|
||||
fetchResults();
|
||||
}
|
||||
};
|
||||
|
||||
const clearAll = () => {
|
||||
searchStore.clearSearch();
|
||||
setSearchQuery('');
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (aiFiltersStore.filtersSetKey !== 0) {
|
||||
searchStore.edit(aiFiltersStore.filters);
|
||||
}
|
||||
}, [aiFiltersStore.filters, aiFiltersStore.filtersSetKey]);
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<Input
|
||||
onChange={onSearchChange}
|
||||
placeholder='E.g., "Sessions with login issues this week"'
|
||||
id="search"
|
||||
onKeyDown={handleKeyDown}
|
||||
value={searchQuery}
|
||||
style={{ minWidth: 360, height: 30 }}
|
||||
autoComplete="off"
|
||||
className="px-4 py-1 text-lg placeholder-lg !border-0 nofocus"
|
||||
leadingButton={
|
||||
searchQuery !== '' ? (
|
||||
<div
|
||||
className="h-full flex items-center cursor-pointer"
|
||||
onClick={hasFilters ? clearAll : fetchResults}
|
||||
>
|
||||
<div className="px-2 py-1 hover:bg-active-blue rounded mr-2">
|
||||
{hasFilters ? <CloseOutlined /> : <EnterOutlined />}
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
function AiSessionSearchField() {
|
||||
const { aiFiltersStore } = useStore();
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-full shadow-sm w-full">
|
||||
<div
|
||||
className={aiFiltersStore.isLoading ? 'animate-bg-spin' : ''}
|
||||
style={gradientBox}
|
||||
>
|
||||
<AiSearchField />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
export const gradientBox = {
|
||||
border: 'double 1.5px transparent',
|
||||
borderRadius: '100px',
|
||||
background:
|
||||
'linear-gradient(#ffffff, #ffffff), linear-gradient(-45deg, #394eff, #3eaaaf, #3ccf65)',
|
||||
backgroundOrigin: 'border-box',
|
||||
backgroundSize: '200% 200%',
|
||||
backgroundClip: 'content-box, border-box',
|
||||
display: 'flex',
|
||||
gap: '0.25rem',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
};
|
||||
|
||||
export default observer(AiSessionSearchField);
|
||||
export default AiSessionSearchField;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { useStore } from 'App/mstore';
|
|||
import SessionCopyLink from './SessionCopyLink';
|
||||
import IntegrateSlackButton from '../IntegrateSlackButton/IntegrateSlackButton';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { signalService } from 'App/services';
|
||||
|
||||
interface Channel {
|
||||
webhookId: string;
|
||||
|
|
@ -126,7 +127,19 @@ const ShareModalComp: React.FC<Props> = ({ showCopyLink, hideModal, time }) => {
|
|||
);
|
||||
|
||||
const sendMsg = async () => {
|
||||
shareTo === 'slack' ? await shareToSlack() : await shareToMSTeams();
|
||||
if (shareTo === 'slack') {
|
||||
await shareToSlack();
|
||||
} else {
|
||||
await shareToMSTeams();
|
||||
}
|
||||
|
||||
signalService.send(
|
||||
{
|
||||
source: 'share',
|
||||
value: shareTo,
|
||||
},
|
||||
sessionId,
|
||||
);
|
||||
};
|
||||
|
||||
const hasBoth = slackOptions.length > 0 && msTeamsOptions.length > 0;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,11 @@ export default class AiFiltersStore {
|
|||
console.log(r);
|
||||
};
|
||||
|
||||
clearFilters = (): void => {
|
||||
this.filters = { filters: [] };
|
||||
this.filtersSetKey = 0;
|
||||
};
|
||||
|
||||
setLoading = (loading: boolean): void => {
|
||||
this.isLoading = loading;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,2 +1,9 @@
|
|||
export default class ClipStore {}
|
||||
export default class ClipStore {
|
||||
prevId = undefined;
|
||||
nextId = undefined;
|
||||
currentId = undefined;
|
||||
|
||||
next() {}
|
||||
prev() {}
|
||||
}
|
||||
// SAAS only
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue