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:
Delirium 2024-04-25 15:28:12 +02:00 committed by GitHub
parent d1c4ba496d
commit f033cbd12f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 174 additions and 91 deletions

View file

@ -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;

View file

@ -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>
);
}

View file

@ -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);

View file

@ -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>
);
}

View file

@ -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'>

View file

@ -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 {

View file

@ -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;

View file

@ -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]);

View file

@ -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));

View file

@ -148,6 +148,7 @@ function FilterList(props: Props) {
</div>
<Segmented
size={'small'}
onChange={(v) =>
props.onChangeEventsOrder(
null,

View file

@ -25,6 +25,7 @@ const customTheme: ThemeConfig = {
},
Segmented: {
itemSelectedBg: '#FFFFFF',
itemSelectedColor: colors['main'],
},
Menu: {
colorPrimary: colors.teal,

View file

@ -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 {

View file

@ -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;
}
}