openreplay/frontend/app/components/Dashboard/components/AddCardSection/AddCardSection.tsx
Delirium 622d0a7dfa
ui: omnisearch, timeseries charts redesign (#2791)
* ui: start redesign for live search/list

* ui: remove search field, show filters picker by default for assist

* ui: filter modal wip

* ui: filter modal wip

* ui: finish with omnisearch thing

* ui: start new dashboard redesign

* refining new card section

* ui: some "new dashboard" view improvs, fix icons fill inheritance, add ai button colors

* ui: split up search component (1.22+ tbd?), restrict filter type to own modals

* ui: mimic ant card

* ui: some changes for card creation flow, add series table to CustomMetricLineChart.tsx

* ui: more chart types, add table with filtering out series, start "compare to" thing

* ui: comparison designs

* ui: better granularity support, comparison view for bar chart

* ui: add comparison to more charts, add "metric" chart (BigNumChart.tsx)

* ui: cleanup logs

* ui: fix defualt import, fix sessheader crash, fix condition set ui

* ui: some refactoring and type coverage...

* ui: more refactoring; silence warnings for list renderers

* ui: moveing and renaming filters

* ui: add metricOf selector

* ui: check for metric type

* ui: fix crashes, add widget library table

* ui: change new series btn

* ui: restrict filterselection

* ui: fix timeseries table format

* ui: autoclose autocomplete modal

* ui: some fixes to issue filters default value, display and placeholder consistency

* ui: some dashboard issues with card selection modal and empty states

* ui: comparing for funnels, alternate column view, some refactoring to prepare for customizations...

* Style improvements in omnisearch headers

* Revert "Style improvements in omnisearch headers"

This reverts commit 89e51b0531.

* ui: show health status fetch error

* ui: table, bignum and comp for funnel, add csv export

* Omni-search improvements. (#2823)

Co-authored-by: Sudheer Salavadi <connect.uxmaster@gmail.com>

* ui: fix bad merge (git hallo?)

* ui: fix filter mapper

* rm husky

* ui: add card floater

* ui: add card floater

* ui: refactor local autocomplete input

* ui: filterout empty options

* UI improvements in New Cards (#2864)

* ui: some minor dashb improvements

* ui: metric type selector for head

* ui: change card type selector, add automapping

* ui: check chart/widget components for crashes

* ui: fix crash with table metrics

* ui: fix crashes related to metric type changes

* ui: filter category for clickmap filt

* ui: fix dash options menu, fix cr/up button

* ui: fix dash list menu propagation

* ui: hide addevent in heatmaps

* ui: fix time mapping for charts

* ui: fix exclusion component for path

* ui: fix series amount for path analysis, rm grid/list selector

* ui: fix icons in list view

* ui: fix for dlt button in widgets

* Various improvements Cards, OmniSearch and Cards  Listing (#2881)

* ui: some improvements for cards list view, funnels and general filter display

* ui: longer node width for journey

* Product Analytics UI Improvements. (#2896)

* Various improvements Cards, OmniSearch and Cards  Listing

* Improved cards listing page

* Various improvements in product analytics

* Charts UI improvements

---------

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

* Live se red s2 (#2902)

* Various improvements Cards, OmniSearch and Cards  Listing

* Improved cards listing page

* Various improvements in product analytics

* Charts UI improvements

* ui crash

---------

Co-authored-by: Sudheer Salavadi <connect.uxmaster@gmail.com>

* ui: fix lucide version

* ui: fix custom comparison period

* ui: fix custom comparison period

* ui: handle minor paths on frontend for path/sankey

* ui: assign icon for event types in sankey nodes

* ui: some strings changed

* ui: hide btn control for table view

* Various improvements in graphs, and analytics pages. (#2908)

* Various improvements Cards, OmniSearch and Cards  Listing

* Improved cards listing page

* Various improvements in product analytics

* Charts UI improvements

* ui crash

* Chart improvements and layout toggling

* Various improvements

* Tooltips

---------

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

* ui: fix weekday mapper for x axis on >7d range

* ui: lower default density to 35, fix table card display

* ui: filterMinorPaths -> return input data if nodes arr. is empty

* ui: use default filter for sessions, move around saved search actions, remove tags modal

* ui: fix card creator visibility in grid, fix table exporter visiblility in grid

* ui: fix some proptype warnings

* ui: change new series default expand state

* ui: save comp range in widget details

* ui: move timeseries to apache echarts

* ui: use unique id for window values

* ui: add timestamp for comp tooltip row

* ui: rename var for readability

* ui: fix comparison for 24hr

* Streamlined icons and improved echarts trends (#2920)

* Various improvements Cards, OmniSearch and Cards  Listing

* Improved cards listing page

* Various improvements in product analytics

* Charts UI improvements

* ui crash

* Chart improvements and layout toggling

* Various improvements

* Tooltips

* Improved icons in cards listing page

* Update WidgetFormNew.tsx

* Sankey improvements

* Icon and text updates

Text alignment and color changes in x-ray
Icon Mapping with appropriate names and shapes

* Colors and Trend Chart Interaction updates

* ui

---------

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

* ui: series update observe

* ui: resize chart on window

* ui: move barchart to echarts

* ui: fixing bars under comparison

* ui: fixing horizontal bar tooltip

* ui: rm unused

* ui: keep state in storage

* ui: small fixes for granularity and comparisons

* ui: fix savesearch button, fix comparison period tracking

* ui: fix funnel type selection

* ui: fixing saved search button

* ui: enable error logging, remove immutable reference

* ui: update savedsearch drop

* ui: disable button if no saved

* ui: small ui fixes

* ui: add drill to summary charts, add more options to card category picker

* ui: filter compSeries with table

* ui: swap tag_el operator and value

* ui: fix top countries

* ui: further changes for search/cards

* ui: move focus to session list on line click

* ui: fix issue filter mapper

* ui: fix alert pre-init function, fix metric list options, fix legend placement

* ui: fixes for card library

* ui: work on new sankey chart

* ui: fix metadata prefetch

* ui: moving snakey to echarts

* ui: fix funnel comparison focus

* ui: stale loader

---------

Co-authored-by: Sudheer Salavadi <connect.uxmaster@gmail.com>
2025-01-24 09:58:35 +01:00

308 lines
8.9 KiB
TypeScript

import React from 'react';
import { FolderOutlined } from '@ant-design/icons';
import { Segmented, Button } from 'antd';
import {
LineChart,
Filter,
ArrowUpDown,
WifiOff,
Turtle,
FileStack,
AppWindow,
Combine,
Users,
Sparkles,
Globe,
MonitorSmartphone,
} from 'lucide-react';
import { Icon } from 'UI';
import FilterSeries from 'App/mstore/types/filterSeries';
import { useModal } from 'App/components/Modal';
import {
CARD_LIST,
CardType,
} from '../DashboardList/NewDashModal/ExampleCards';
import { useStore } from 'App/mstore';
import {
HEATMAP,
FUNNEL,
TABLE,
TIMESERIES,
USER_PATH,
CATEGORIES,
} from 'App/constants/card';
import { useHistory } from 'react-router-dom';
import { dashboardMetricCreate, withSiteId, metricCreate } from 'App/routes';
import { FilterKey } from 'Types/filter/filterType';
import MetricsLibraryModal from '../MetricsLibraryModal/MetricsLibraryModal';
import { observer } from 'mobx-react-lite';
interface TabItem {
icon: React.ReactNode;
title: string;
description: string;
type: string;
}
export const tabItems: Record<string, TabItem[]> = {
[CATEGORIES.product_analytics]: [
{
icon: <LineChart width={16} />,
title: 'Trends',
type: TIMESERIES,
description: 'Track session trends over time.',
},
{
icon: <Filter width={16} />,
title: 'Funnels',
type: FUNNEL,
description: 'Visualize user progression through critical steps.',
},
{
icon: (
<Icon name={'dashboards/user-journey'} color={'inherit'} size={16} />
),
title: 'Journeys',
type: USER_PATH,
description: 'Understand the paths users take through your product.',
},
// { TODO: 1.23+
// icon: <Icon name={'dashboards/cohort-chart'} color={'inherit'} size={16} />,
// title: 'Retention',
// type: RETENTION,
// description: 'Analyze user retention over specific time periods.',
// },
{
icon: <Icon name={'dashboards/heatmap-2'} color={'inherit'} size={16} />,
title: 'Heatmaps',
type: HEATMAP,
description: 'Visualize user interaction patterns on your pages.',
},
],
[CATEGORIES.monitors]: [
{
icon: (
<Icon name={'dashboards/circle-alert'} color={'inherit'} size={16} />
),
title: 'JS Errors',
type: FilterKey.ERRORS,
description: 'Monitor JS errors affecting user experience.',
},
{
icon: <ArrowUpDown width={16} />,
title: 'Top Network Requests',
type: FilterKey.FETCH,
description: 'Identify the most frequent network requests.',
},
{
icon: <WifiOff width={16} />,
title: '4xx/5xx Requests',
type: TIMESERIES + '_4xx_requests',
description: 'Track client and server errors for performance issues.',
},
{
icon: <Turtle width={16} />,
title: 'Slow Network Requests',
type: TIMESERIES + '_slow_network_requests',
description: 'Pinpoint the slowest network requests causing delays.',
},
],
[CATEGORIES.web_analytics]: [
{
icon: <FileStack width={16} />,
title: 'Top Pages',
type: FilterKey.LOCATION,
description: 'Discover the most visited pages on your site.',
},
{
icon: <AppWindow width={16} />,
title: 'Top Browsers',
type: FilterKey.USER_BROWSER,
description: 'Analyze the browsers your visitors are using the most.',
},
{
icon: <Combine width={16} />,
title: 'Top Referrer',
type: FilterKey.REFERRER,
description: 'See where your traffic is coming from.',
},
{
icon: <Users width={16} />,
title: 'Top Users',
type: FilterKey.USERID,
description: 'Identify the users with the most interactions.',
},
{
icon: <Globe width={16} />,
title: 'Top Countries',
type: FilterKey.USER_COUNTRY,
description: 'Track the geographical distribution of your audience.',
},
{
icon: <MonitorSmartphone width={16} />,
title: 'Top Devices',
type: FilterKey.USER_DEVICE,
description: 'Explore the devices used by your users.',
}
// { TODO: 1.23+ maybe
// icon: <ArrowDown10 width={16} />,
// title: 'Speed Index by Country',
// type: TABLE,
// description: 'Measure performance across different regions.',
// },
],
};
function CategoryTab({ tab, inCards }: { tab: string; inCards?: boolean }) {
const items = tabItems[tab];
const { metricStore, projectsStore, dashboardStore } = useStore();
const history = useHistory();
const handleCardSelection = (card: string) => {
metricStore.init();
const selectedCard = CARD_LIST.find((c) => c.key === card) as CardType;
const cardData: any = {
metricType: selectedCard.cardType,
name: selectedCard.title,
metricOf: selectedCard.metricOf,
category: card,
};
if (selectedCard.filters) {
cardData.series = [
new FilterSeries().fromJson({
name: 'Series 1',
filter: {
filters: selectedCard.filters,
},
}),
];
}
// TODO This code here makes 0 sense
if (selectedCard.cardType === FUNNEL) {
cardData.series = [];
cardData.series.push(new FilterSeries());
cardData.series[0].filter.addFunnelDefaultFilters();
cardData.series[0].filter.eventsOrder = 'then';
cardData.series[0].filter.eventsOrderSupport = ['then'];
}
metricStore.setCardCategory(tab);
metricStore.merge(cardData);
if (projectsStore.activeSiteId) {
if (inCards) {
history.push(withSiteId(metricCreate(), projectsStore.activeSiteId));
} else if (dashboardStore.selectedDashboard) {
history.push(
withSiteId(
dashboardMetricCreate(dashboardStore.selectedDashboard.dashboardId),
projectsStore.activeSiteId
)
);
}
}
};
return (
<div className={'flex flex-col gap-3'}>
{items.map((item, index) => (
<div
onClick={() => handleCardSelection(item.type)}
key={index}
className={
'flex items-start gap-2 p-2 hover:bg-active-blue rounded-xl hover:text-teal group cursor-pointer'
}
>
{item.icon}
<div className={'leading-none'}>
<div>{item.title}</div>
<div className={'text-disabled-text group-hover:text-teal/60 text-sm'}>
{item.description}
</div>
</div>
</div>
))}
</div>
);
}
const AddCardSection = observer(
({ inCards, handleOpenChange }: { inCards?: boolean, handleOpenChange?: (isOpen: boolean) => void }) => {
const { showModal } = useModal();
const { metricStore, dashboardStore, projectsStore } = useStore();
const [tab, setTab] = React.useState('product_analytics');
const options = [
{ label: 'Product Analytics', value: 'product_analytics' },
{ label: 'Monitors', value: 'monitors' },
{ label: 'Web Analytics', value: 'web_analytics' },
];
const originStr = window.env.ORIGIN || window.location.origin;
const isSaas = /api\.openreplay\.com/.test(originStr);
const onExistingClick = () => {
const dashboardId = dashboardStore.selectedDashboard?.dashboardId;
const siteId = projectsStore.activeSiteId;
showModal(
<MetricsLibraryModal siteId={siteId} dashboardId={dashboardId} />,
{
right: true,
width: 800,
onClose: () => {
metricStore.updateKey('metricsSearch', '');
},
}
);
handleOpenChange?.(false);
};
return (
<div
className={
'pt-4 pb-6 px-6 rounded-xl bg-white border border-gray-lighter flex flex-col gap-2'
}
>
<div className={'flex justify-between p-2'}>
<div className={'text-xl font-medium mb-1'}>
What do you want to visualize?
</div>
{isSaas ? (
<div
className={'font-medium flex items-center gap-2 cursor-pointer'}
>
<Sparkles color={'#3C00FFD8'} size={16} />
<div className={'ai-gradient'}>Ask AI</div>
</div>
) : null}
</div>
<div>
<Segmented
options={options}
value={tab}
onChange={(value) => setTab(value)}
/>
</div>
<div className="py-2">
<CategoryTab tab={tab} inCards={inCards} />
</div>
{inCards ? null :
<div
className={
'w-full flex items-center justify-center border-t mt-auto border-t-gray-lighter gap-2 pt-2 cursor-pointer'
}
>
<Button
className="w-full mt-4 hover:bg-active-blue hover:text-teal"
type="text"
variant="text"
onClick={onExistingClick}
>
<FolderOutlined /> Add existing card
</Button>
</div>
}
</div>
);
}
);
export default AddCardSection;