fix ui: fixes for segment comp + ui mockup (#2137)
* fix ui: fixes for segment comp * feat ui: some ai testing mockup
This commit is contained in:
parent
d1c4ba496d
commit
f033cbd12f
13 changed files with 174 additions and 91 deletions
|
|
@ -160,7 +160,13 @@ export default class APIClient {
|
|||
edp = `${edp}/${this.siteId}`;
|
||||
}
|
||||
|
||||
if (path.includes('login') || path.includes('refresh') || path.includes('logout')) {
|
||||
if (
|
||||
(
|
||||
path.includes('login')
|
||||
|| path.includes('refresh')
|
||||
|| path.includes('logout')
|
||||
) && window.env.NODE_ENV !== 'development'
|
||||
) {
|
||||
init.credentials = 'include';
|
||||
} else {
|
||||
delete init.credentials;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import React from 'react';
|
||||
import { Icon } from 'UI';
|
||||
import { Segmented } from 'antd';
|
||||
import cn from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
import { Icon } from 'UI';
|
||||
|
||||
|
||||
interface Props {
|
||||
onChange: any;
|
||||
|
|
@ -10,31 +13,29 @@ interface Props {
|
|||
|
||||
const allItem = { key: 'all', title: 'All' };
|
||||
|
||||
function FilterButton(props: { activeItem: string, item: any, onClick: () => any }) {
|
||||
return <div
|
||||
className={cn('cursor-pointer transition group rounded px-2 py-1 flex items-center uppercase text-sm hover:bg-active-blue hover:text-teal', {
|
||||
'bg-active-blue text-teal': props.activeItem === props.item.key
|
||||
})}
|
||||
style={{ height: '36px' }}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
{props.item.icon && <Icon name={props.item.icon} className='mr-2' />}
|
||||
<span>{props.item.title}</span>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function IntegrationFilters(props: Props) {
|
||||
|
||||
const segmentItems = [allItem, ...props.filters].map((item: any) => ({
|
||||
key: item.key,
|
||||
value: item.key,
|
||||
label: (
|
||||
<div className={'flex items-center gap-2'}>
|
||||
{item.icon ? <Icon name={item.icon} color={'inherit'} /> : null}
|
||||
<div>{item.title}</div>
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
const onChange = (val) => {
|
||||
props.onChange(val)
|
||||
}
|
||||
return (
|
||||
<div className='flex items-center gap-4'>
|
||||
<FilterButton
|
||||
activeItem={props.activeItem}
|
||||
item={allItem}
|
||||
onClick={() => props.onChange(allItem.key)}
|
||||
<Segmented
|
||||
value={props.activeItem}
|
||||
onChange={onChange}
|
||||
options={segmentItems}
|
||||
/>
|
||||
{props.filters.map((item: any) => (
|
||||
<FilterButton activeItem={props.activeItem} item={item} onClick={() => props.onChange(item.key)} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,8 +98,9 @@ function Integrations(props: Props) {
|
|||
const filters = integrations.map((cat: any) => ({
|
||||
key: cat.key,
|
||||
title: cat.title,
|
||||
label: cat.title,
|
||||
icon: cat.icon
|
||||
}));
|
||||
}))
|
||||
|
||||
|
||||
const allIntegrations = filteredIntegrations.flatMap(cat => cat.integrations);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import { PageTitle, Button, Link, Toggler } from 'UI';
|
||||
import { PageTitle, Button, Toggler, Icon } from "UI";
|
||||
import { Segmented } from 'antd';
|
||||
import MetricsSearch from '../MetricsSearch';
|
||||
import Select from 'Shared/Select';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
|
@ -102,25 +103,33 @@ function DashboardDropdown({ onChange, plain = false }: { plain?: boolean; onCha
|
|||
);
|
||||
}
|
||||
|
||||
function ListViewToggler({}) {
|
||||
function ListViewToggler() {
|
||||
const { metricStore } = useStore();
|
||||
const listView = useObserver(() => metricStore.listView);
|
||||
return (
|
||||
<div className='flex items-center'>
|
||||
<Button
|
||||
icon='list-alt'
|
||||
variant={listView ? 'text-primary' : 'text'}
|
||||
onClick={() => metricStore.updateKey('listView', true)}
|
||||
>
|
||||
List
|
||||
</Button>
|
||||
<Button
|
||||
icon='grid'
|
||||
variant={!listView ? 'text-primary' : 'text'}
|
||||
onClick={() => metricStore.updateKey('listView', false)}
|
||||
>
|
||||
Grid
|
||||
</Button>
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
label: <div className={'flex items-center gap-2'}>
|
||||
<Icon name={'list-alt'} color={'inherit'} />
|
||||
<div>List</div>
|
||||
</div>,
|
||||
value: 'list'
|
||||
},
|
||||
{
|
||||
label: <div className={'flex items-center gap-2'}>
|
||||
<Icon name={'grid'} color={'inherit'} />
|
||||
<div>Grid</div>
|
||||
</div>,
|
||||
value: 'grid'
|
||||
}
|
||||
]}
|
||||
onChange={(val) => {
|
||||
metricStore.updateKey('listView', val === 'list')
|
||||
}}
|
||||
value={listView ? 'list' : 'grid'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ function WidgetForm(props: Props) {
|
|||
}
|
||||
} = props;
|
||||
const [aiQuery, setAiQuery] = useState('')
|
||||
const [aiAskChart, setAiAskChart] = useState('')
|
||||
const { metricStore, dashboardStore, aiFiltersStore } = useStore();
|
||||
const isSaving = metricStore.isSaving;
|
||||
const metric: any = metricStore.instance;
|
||||
|
|
@ -139,11 +140,20 @@ function WidgetForm(props: Props) {
|
|||
})
|
||||
};
|
||||
|
||||
const fetchChartData = () => {
|
||||
void aiFiltersStore.getCardData(aiAskChart, metric.toJson())
|
||||
}
|
||||
|
||||
const handleKeyDown = (event: any) => {
|
||||
if (event.key === 'Enter') {
|
||||
fetchResults();
|
||||
}
|
||||
};
|
||||
const handleChartKeyDown = (event: any) => {
|
||||
if (event.key === 'Enter') {
|
||||
fetchChartData();
|
||||
}
|
||||
};
|
||||
|
||||
const testingKey = localStorage.getItem('__mauricio_testing_access') === 'true';
|
||||
return (
|
||||
|
|
@ -260,6 +270,13 @@ function WidgetForm(props: Props) {
|
|||
className="w-full mb-2"
|
||||
onKeyDown={handleKeyDown}
|
||||
/> : null}
|
||||
{testingKey ? <Input
|
||||
placeholder="AI Ask Chart"
|
||||
value={aiAskChart}
|
||||
onChange={(e: any) => setAiAskChart(e.target.value)}
|
||||
className="w-full mb-2"
|
||||
onKeyDown={handleChartKeyDown}
|
||||
/> : null}
|
||||
{aiFiltersStore.isLoading ? (
|
||||
<div>
|
||||
<div className='flex items-center font-medium py-2'>
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import Session from 'Types/session'
|
|||
import PlayerBlock from './PlayerBlock';
|
||||
|
||||
const TABS = {
|
||||
EVENTS: 'User Events',
|
||||
HEATMAPS: 'Click Map',
|
||||
EVENTS: 'Activity',
|
||||
HEATMAPS: 'Click map',
|
||||
};
|
||||
|
||||
interface IProps {
|
||||
|
|
|
|||
|
|
@ -8,11 +8,6 @@ import { PlayerContext } from 'Components/Session/playerContext';
|
|||
import Session from 'Types/session'
|
||||
import PlayerBlock from './PlayerBlock';
|
||||
|
||||
const TABS = {
|
||||
EVENTS: 'User Events',
|
||||
HEATMAPS: 'Click Map',
|
||||
};
|
||||
|
||||
interface IProps {
|
||||
fullscreen: boolean;
|
||||
activeTab: string;
|
||||
|
|
|
|||
|
|
@ -25,9 +25,10 @@ import { IPlayerContext, PlayerContext, defaultContextValue } from './playerCont
|
|||
|
||||
const TABS = {
|
||||
EVENTS: 'Activity',
|
||||
CLICKMAP: 'Click Map',
|
||||
CLICKMAP: 'Click map',
|
||||
INSPECTOR: 'Tag'
|
||||
};
|
||||
|
||||
const UXTTABS = {
|
||||
EVENTS: TABS.EVENTS
|
||||
};
|
||||
|
|
@ -141,7 +142,7 @@ function WebPlayer(props: any) {
|
|||
}, [cssLoading, ready])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (activeTab === 'Click Map') {
|
||||
if (activeTab === 'Click map') {
|
||||
contextValue.player?.pause();
|
||||
}
|
||||
}, [activeTab]);
|
||||
|
|
|
|||
|
|
@ -1,24 +1,29 @@
|
|||
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 { Segmented } from 'antd'
|
||||
|
||||
import { MobilePlayerContext, PlayerContext } from 'App/components/Session/playerContext';
|
||||
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 { toggleBottomBlock, setZoomTab } from 'Duck/components/player';
|
||||
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 FeatureSelection, {
|
||||
HELP_MESSAGE,
|
||||
} from './components/FeatureSelection/FeatureSelection';
|
||||
import OverviewPanelContainer from './components/OverviewPanelContainer';
|
||||
import TimelinePointer from './components/TimelinePointer';
|
||||
import TimelineScale from './components/TimelineScale';
|
||||
import VerticalPointerLine from './components/VerticalPointerLine';
|
||||
import TimelineZoomButton from 'Components/Session_/Player/Controls/components/TimelineZoomButton';
|
||||
|
||||
function MobileOverviewPanelCont({
|
||||
issuesList,
|
||||
|
|
@ -27,7 +32,7 @@ function MobileOverviewPanelCont({
|
|||
zoomStartTs,
|
||||
zoomEndTs,
|
||||
setZoomTab,
|
||||
zoomTab
|
||||
zoomTab,
|
||||
}: {
|
||||
issuesList: Record<string, any>[];
|
||||
sessionId: string;
|
||||
|
|
@ -35,7 +40,7 @@ function MobileOverviewPanelCont({
|
|||
zoomStartTs: number;
|
||||
zoomEndTs: number;
|
||||
setZoomTab: (tab: string) => void;
|
||||
zoomTab: 'overview' | 'journey' | 'issues' | 'errors'
|
||||
zoomTab: 'overview' | 'journey' | 'issues' | 'errors';
|
||||
}) {
|
||||
const { aiSummaryStore } = useStore();
|
||||
const { store, player } = React.useContext(MobilePlayerContext);
|
||||
|
|
@ -60,7 +65,9 @@ function MobileOverviewPanelCont({
|
|||
const fetchPresented = fetchList.length > 0;
|
||||
|
||||
const checkInZoomRange = (list: any[]) => {
|
||||
return list.filter((i) => (zoomEnabled ? i.time >= zoomStartTs && i.time <= zoomEndTs : true));
|
||||
return list.filter((i) =>
|
||||
zoomEnabled ? i.time >= zoomStartTs && i.time <= zoomEndTs : true
|
||||
);
|
||||
};
|
||||
|
||||
const resources = {
|
||||
|
|
@ -87,7 +94,13 @@ function MobileOverviewPanelCont({
|
|||
) {
|
||||
setDataLoaded(true);
|
||||
}
|
||||
}, [issuesList, exceptionsList, eventsList, performanceChartData, frustrationsList]);
|
||||
}, [
|
||||
issuesList,
|
||||
exceptionsList,
|
||||
eventsList,
|
||||
performanceChartData,
|
||||
frustrationsList,
|
||||
]);
|
||||
|
||||
React.useEffect(() => {
|
||||
player.scale();
|
||||
|
|
@ -106,7 +119,9 @@ function MobileOverviewPanelCont({
|
|||
performanceList={performanceList}
|
||||
sessionId={sessionId}
|
||||
showSummary={isSaas}
|
||||
toggleSummary={() => aiSummaryStore.setToggleSummary(!aiSummaryStore.toggleSummary)}
|
||||
toggleSummary={() =>
|
||||
aiSummaryStore.setToggleSummary(!aiSummaryStore.toggleSummary)
|
||||
}
|
||||
summaryChecked={aiSummaryStore.toggleSummary}
|
||||
setZoomTab={setZoomTab}
|
||||
zoomTab={zoomTab}
|
||||
|
|
@ -127,7 +142,7 @@ function WebOverviewPanelCont({
|
|||
zoomStartTs: number;
|
||||
zoomEndTs: number;
|
||||
setZoomTab: (tab: string) => void;
|
||||
zoomTab: 'overview' | 'journey' | 'issues' | 'errors'
|
||||
zoomTab: 'overview' | 'journey' | 'issues' | 'errors';
|
||||
}) {
|
||||
const { aiSummaryStore } = useStore();
|
||||
const { store } = React.useContext(PlayerContext);
|
||||
|
|
@ -146,7 +161,8 @@ function WebOverviewPanelCont({
|
|||
const resourceListUnmap = tabStates[currentTab]?.resourceList || [];
|
||||
const fetchList = tabStates[currentTab]?.fetchList || [];
|
||||
const graphqlList = tabStates[currentTab]?.graphqlList || [];
|
||||
const performanceChartData = tabStates[currentTab]?.performanceChartData || [];
|
||||
const performanceChartData =
|
||||
tabStates[currentTab]?.performanceChartData || [];
|
||||
|
||||
const fetchPresented = fetchList.length > 0;
|
||||
const resourceList = resourceListUnmap
|
||||
|
|
@ -158,7 +174,9 @@ function WebOverviewPanelCont({
|
|||
.filter((i: any) => i.type === 'fetch');
|
||||
|
||||
const checkInZoomRange = (list: any[]) => {
|
||||
return list.filter((i) => (zoomEnabled ? i.time >= zoomStartTs && i.time <= zoomEndTs : true));
|
||||
return list.filter((i) =>
|
||||
zoomEnabled ? i.time >= zoomStartTs && i.time <= zoomEndTs : true
|
||||
);
|
||||
};
|
||||
|
||||
const resources: any = React.useMemo(() => {
|
||||
|
|
@ -181,7 +199,9 @@ function WebOverviewPanelCont({
|
|||
fetchPresented={fetchPresented}
|
||||
setSelectedFeatures={setSelectedFeatures}
|
||||
showSummary={isSaas}
|
||||
toggleSummary={() => aiSummaryStore.setToggleSummary(!aiSummaryStore.toggleSummary)}
|
||||
toggleSummary={() =>
|
||||
aiSummaryStore.setToggleSummary(!aiSummaryStore.toggleSummary)
|
||||
}
|
||||
summaryChecked={aiSummaryStore.toggleSummary}
|
||||
sessionId={sessionId}
|
||||
setZoomTab={setZoomTab}
|
||||
|
|
@ -213,35 +233,44 @@ function PanelComponent({
|
|||
<span className={'font-semibold text-black'}>X-Ray</span>
|
||||
{showSummary ? (
|
||||
<>
|
||||
<SummaryButton withToggle onClick={toggleSummary} toggleValue={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',
|
||||
}
|
||||
]}
|
||||
<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>
|
||||
<div className="flex items-center h-20 mr-4 gap-2">
|
||||
<TimelineZoomButton />
|
||||
<FeatureSelection list={selectedFeatures} updateList={setSelectedFeatures} />
|
||||
<FeatureSelection
|
||||
list={selectedFeatures}
|
||||
updateList={setSelectedFeatures}
|
||||
/>
|
||||
</div>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content className={'overflow-y-auto'}>
|
||||
|
|
@ -266,7 +295,9 @@ function PanelComponent({
|
|||
{selectedFeatures.map((feature: any, index: number) => (
|
||||
<div
|
||||
key={feature}
|
||||
className={cn('border-b last:border-none relative', { 'bg-white': index % 2 })}
|
||||
className={cn('border-b last:border-none relative', {
|
||||
'bg-white': index % 2,
|
||||
})}
|
||||
>
|
||||
<EventRow
|
||||
isGraph={feature === 'PERFORMANCE'}
|
||||
|
|
@ -283,7 +314,11 @@ function PanelComponent({
|
|||
message={HELP_MESSAGE[feature]}
|
||||
/>
|
||||
{isMobile && feature === 'PERFORMANCE' ? (
|
||||
<div className={'absolute top-0 left-0 flex items-center py-4 w-full'}>
|
||||
<div
|
||||
className={
|
||||
'absolute top-0 left-0 flex items-center py-4 w-full'
|
||||
}
|
||||
>
|
||||
<EventRow
|
||||
isGraph={false}
|
||||
title={''}
|
||||
|
|
@ -321,7 +356,8 @@ export const OverviewPanel = connect(
|
|||
zoomEndTs: state.getIn(['components', 'player']).timelineZoom.endTs,
|
||||
}),
|
||||
{
|
||||
toggleBottomBlock, setZoomTab
|
||||
toggleBottomBlock,
|
||||
setZoomTab,
|
||||
}
|
||||
)(observer(WebOverviewPanelCont));
|
||||
|
||||
|
|
@ -332,9 +368,10 @@ export const MobileOverviewPanel = connect(
|
|||
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
|
||||
zoomTab: state.getIn(['components', 'player']).zoomTab,
|
||||
}),
|
||||
{
|
||||
toggleBottomBlock, setZoomTab,
|
||||
toggleBottomBlock,
|
||||
setZoomTab,
|
||||
}
|
||||
)(observer(MobileOverviewPanelCont));
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ function FilterList(props: Props) {
|
|||
</div>
|
||||
|
||||
<Segmented
|
||||
size={'small'}
|
||||
onChange={(v) =>
|
||||
props.onChangeEventsOrder(
|
||||
null,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ const customTheme: ThemeConfig = {
|
|||
},
|
||||
Segmented: {
|
||||
itemSelectedBg: '#FFFFFF',
|
||||
itemSelectedColor: colors['main'],
|
||||
},
|
||||
Menu: {
|
||||
colorPrimary: colors.teal,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,11 @@ export default class AiFiltersStore {
|
|||
this.filtersSetKey += 1;
|
||||
};
|
||||
|
||||
getCardData = async (query: string, chartData: Record<string, any>) => {
|
||||
const r = await aiService.getCardData(query, chartData);
|
||||
console.log(r)
|
||||
}
|
||||
|
||||
getCardFilters = async (query: string, chartType: string): Promise<any> => {
|
||||
this.isLoading = true;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -40,4 +40,13 @@ export default class AiService extends BaseService {
|
|||
const { data } = await r.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
async getCardData(query: string, chartData: Record<string, any>): Promise<Record<string, any>> {
|
||||
const r = await this.client.post('/intelligent/search-plus', {
|
||||
...chartData,
|
||||
question: query,
|
||||
});
|
||||
const { data } = await r.json();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue