openreplay/frontend/app/components/Dashboard/components/WidgetForm/WidgetFormNew.tsx
Delirium d604f9920b
feat ui: dashboards redesign (#2230)
* feat ui: dashboards redesign start

* more cards

* fix ui: more different cards...

* feat ui: finish cards, all trigger, all icons

* change(ui): added missin const

* feature(ui): new dashboard modal

* feature(ui): new dashboard modal

* change(ui): new cards

* change(ui): dashboard redesign

* change(ui): dashboard redesign

* change(ui): dashboard redesign

* change(ui): modal context and alert form

* change(ui): table card show more with modal

* change(ui): examples

* change(ui): example categorize and other improvements

* change(ui): example categorize and other improvements

* change(ui): performance cards

* change(ui): insights card

* Various style updates in dashboards and other pages. (#2308)

* Various minor style updates

* Various style improvements

* Update ExampleCards.tsx

* change(ui): fixed an issue with card create

* change(ui): fixed an issue with card create

* change(ui): default filters and events order

* change(ui): random data

* Dashboards redesign - improvments (#2313)

* Various minor style updates

* Various style improvements

* Update ExampleCards.tsx

* various minor improvements in dashbaords.

* revised dashboard widget header

* change(ui): sessions by user

* change(ui): funnel example

* change(ui): modal height and scroll

* change(ui): example cards with data

* change(ui): example cards with data

* change(ui): funnel bar text color

* change(ui): example cards overlay click

* change(ui): path analysis filter card

---------

Co-authored-by: Shekar Siri <sshekarsiri@gmail.com>
Co-authored-by: Sudheer Salavadi <connect.uxmaster@gmail.com>
2024-06-27 19:47:34 +02:00

179 lines
6.9 KiB
TypeScript

import React from 'react';
import {Card, Space, Typography, Button} from "antd";
import {useStore} from "App/mstore";
import {eventKeys} from "Types/filter/newFilter";
import {
CLICKMAP,
ERRORS,
FUNNEL,
INSIGHTS,
PERFORMANCE,
RESOURCE_MONITORING,
RETENTION,
TABLE,
USER_PATH, WEB_VITALS
} from "App/constants/card";
import FilterSeries from "Components/Dashboard/components/FilterSeries/FilterSeries";
import {metricOf} from "App/constants/filterOptions";
import {AudioWaveform, ChevronDown, ChevronUp, PlusIcon} from "lucide-react";
import {observer} from "mobx-react-lite";
import AddStepButton from "Components/Dashboard/components/FilterSeries/AddStepButton";
import {Icon} from "UI";
import FilterItem from "Shared/Filters/FilterItem";
import {FilterKey} from "Types/filter/filterType";
function WidgetFormNew() {
const {metricStore, dashboardStore, aiFiltersStore} = useStore();
const metric: any = metricStore.instance;
const eventsLength = metric.series[0].filter.filters.filter((i: any) => i && i.isEvent).length;
const filtersLength = metric.series[0].filter.filters.filter((i: any) => i && !i.isEvent).length;
const isClickMap = metric.metricType === CLICKMAP;
const isPathAnalysis = metric.metricType === USER_PATH;
const excludeFilterKeys = isClickMap || isPathAnalysis ? eventKeys : [];
const hasFilters = filtersLength > 0 || eventsLength > 0;
const isPredefined = [ERRORS, PERFORMANCE, RESOURCE_MONITORING, WEB_VITALS].includes(metric.metricType);
return isPredefined ? <PredefinedMessage/> : (
<Space direction="vertical" className="w-full">
<AdditionalFilters/>
<Card
styles={{
body: {padding: '0'},
cover: {}
}}
>
{!hasFilters && (
<DefineSteps metric={metric} excludeFilterKeys={excludeFilterKeys}/>
)}
</Card>
{hasFilters && (
<FilterSection metric={metric} excludeFilterKeys={excludeFilterKeys}/>
)}
</Space>
);
}
export default observer(WidgetFormNew);
function DefineSteps({metric, excludeFilterKeys}: any) {
return (
<Space className="px-4 py-2 rounded-lg shadow-sm">
<Typography.Text strong>Define Steps</Typography.Text>
<AddStepButton excludeFilterKeys={excludeFilterKeys} series={metric.series[0]}/>
</Space>
);
}
const FilterSection = observer(({metric, excludeFilterKeys}: any) => {
// const timeseriesOptions = metricOf.filter((i) => i.type === 'timeseries');
// const tableOptions = metricOf.filter((i) => i.type === 'table');
const isTable = metric.metricType === TABLE;
const isClickMap = metric.metricType === CLICKMAP;
const isFunnel = metric.metricType === FUNNEL;
const isInsights = metric.metricType === INSIGHTS;
const isPathAnalysis = metric.metricType === USER_PATH;
const isRetention = metric.metricType === RETENTION;
const canAddSeries = metric.series.length < 3;
const eventsLength = metric.series[0].filter.filters.filter((i: any) => i && i.isEvent).length;
// const cannotSaveFunnel = isFunnel && (!metric.series[0] || eventsLength <= 1);
const isSingleSeries = isTable || isFunnel || isClickMap || isInsights || isRetention
// const onAddFilter = (filter: any) => {
// metric.series[0].filter.addFilter(filter);
// metric.updateKey('hasChanged', true)
// }
return (
<>
{
metric.series.length > 0 && metric.series
.slice(0, isSingleSeries ? 1 : metric.series.length)
.map((series: any, index: number) => (
<div className='mb-2' key={series.name}>
<FilterSeries
canExclude={isPathAnalysis}
supportsEmpty={!isClickMap && !isPathAnalysis}
excludeFilterKeys={excludeFilterKeys}
observeChanges={() => metric.updateKey('hasChanged', true)}
hideHeader={isTable || isClickMap || isInsights || isPathAnalysis || isFunnel}
seriesIndex={index}
series={series}
onRemoveSeries={() => metric.removeSeries(index)}
canDelete={metric.series.length > 1}
emptyMessage={
isTable
? 'Filter data using any event or attribute. Use Add Step button below to do so.'
: 'Add user event or filter to define the series by clicking Add Step.'
}
expandable={isSingleSeries}
/>
</div>
))
}
{!isSingleSeries && canAddSeries && (
<Card styles={{body: {padding: '4px'}}}>
<Button
type='link'
onClick={() => {
metric.addSeries();
}}
disabled={!canAddSeries}
size="small"
>
<Space>
<AudioWaveform size={16}/>
New Chart Series
</Space>
</Button>
</Card>
)}
</>
);
})
const PathAnalysisFilter = observer(({metric}: any) => (
<Card styles={{
body: {padding: '4px 20px'},
header: {padding: '4px 20px', fontSize: '14px', fontWeight: 'bold', borderBottom: 'none'},
}}
title={metric.startType === 'start' ? 'Start Point' : 'End Point'}
>
<div className='form-group flex flex-col'>
<FilterItem
hideDelete
filter={metric.startPoint}
allowedFilterKeys={[FilterKey.LOCATION, FilterKey.CLICK, FilterKey.INPUT, FilterKey.CUSTOM]}
onUpdate={val => metric.updateStartPoint(val)}
onRemoveFilter={() => {
}}
/>
</div>
</Card>
));
const AdditionalFilters = observer(() => {
const {metricStore, dashboardStore, aiFiltersStore} = useStore();
const metric: any = metricStore.instance;
return (
<Space direction="vertical" className="w-full">
{metric.metricType === USER_PATH && <PathAnalysisFilter metric={metric}/>}
</Space>
)
});
const PredefinedMessage = () => (
<div className='flex items-center my-6 justify-center'>
<Icon name='info-circle' size='18' color='gray-medium'/>
<div className='ml-2'>Filtering and drill-downs will be supported soon for this card type.</div>
</div>
);