ui: fix crashes related to metric type changes

This commit is contained in:
nick-delirium 2024-12-13 12:04:00 +01:00
parent 2136f87a27
commit 34a51adfc2
No known key found for this signature in database
GPG key ID: 93ABD695DF5FDBA0
7 changed files with 112 additions and 58 deletions

View file

@ -15,7 +15,7 @@ function ClickMapCard() {
const sessionId = metricStore.instance.data.sessionId;
const url = metricStore.instance.data.path;
const operator = metricStore.instance.series[0].filter.filters[0].operator
const operator = metricStore.instance.series[0]?.filter.filters[0]?.operator ? metricStore.instance.series[0].filter.filters[0].operator : 'startsWith'
React.useEffect(() => {

View file

@ -150,6 +150,7 @@ function CategoryTab({ tab, inCards }: { tab: string; inCards?: boolean }) {
metricType: selectedCard.cardType,
name: selectedCard.title,
metricOf: selectedCard.metricOf,
category: card,
};
if (selectedCard.filters) {
@ -166,12 +167,14 @@ function CategoryTab({ tab, inCards }: { tab: string; inCards?: boolean }) {
// TODO This code here makes 0 sense
if (selectedCard.cardType === FUNNEL) {
cardData.series = [];
cardData.series.filter = [];
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);
metricStore.instance.resetDefaults();
if (projectsStore.activeSiteId) {
if (inCards) {

View file

@ -6,6 +6,7 @@ import AddCardSection from '../AddCardSection/AddCardSection';
import cn from 'classnames';
import { Button, Popover } from 'antd'
import { PlusOutlined } from '@ant-design/icons'
import { Loader } from 'UI';
interface Props {
siteId: string;

View file

@ -6,9 +6,12 @@ import { tabItems } from 'Components/Dashboard/components/AddCardSection/AddCard
import {
CARD_LIST,
} from 'Components/Dashboard/components/DashboardList/NewDashModal/ExampleCards';
import FilterSeries from "App/mstore/types/filterSeries";
import { FUNNEL, USER_PATH } from "App/constants/card";
function MetricTypeSelector() {
const { metricStore } = useStore();
const [selectedCategory, setSelectedCategory] = React.useState<string | null>(null);
const metric = metricStore.instance;
const cardCategory = metricStore.cardCategory;
if (!cardCategory) {
@ -18,22 +21,60 @@ function MetricTypeSelector() {
value: opt.type,
icon: opt.icon,
}))
React.useEffect(() => {
const selected = metric.category ? { value: metric.category } : options.find(
(i) => {
if (metric.metricType === 'table') {
return i.value === metric.metricOf;
}
return i.value === metric.metricType
})
if (selected) {
setSelectedCategory(selected.value);
}
}, [])
const onChange = (type: string) => {
const selectedCard = CARD_LIST.find((i) => i.key === type);
if (selectedCard) {
metricStore.changeType(selectedCard.cardType, selectedCard.metricOf);
setSelectedCategory(type);
metricStore.init();
const cardData: Record<string, any> = {
metricType: selectedCard.cardType,
name: selectedCard.title,
metricOf: selectedCard.metricOf,
series: [new FilterSeries()],
category: type,
}
if (selectedCard.filters) {
cardData.series = [
new FilterSeries().fromJson({
name: "Series 1",
filter: {
filters: selectedCard.filters,
}
})
];
}
if (selectedCard.cardType === USER_PATH) {
cardData.series = [];
cardData.series.push(new FilterSeries());
}
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.merge(cardData);
}
};
const selected = options.find(
(i) => {
if (metric.metricType === 'table') {
return i.value === metric.metricOf;
}
return i.value === metric.metricType
}) || options[0];
return (
<Segmented onChange={onChange} options={options} value={selected.value} />
<Segmented onChange={onChange} options={options} value={selectedCategory} />
);
}

View file

@ -51,7 +51,7 @@ function WidgetChart(props: Props) {
});
const { isSaved = false, metric, isTemplate } = props;
const { dashboardStore, metricStore } = useStore();
const _metric: any = metricStore.instance;
const _metric: any = props.isPreview ? metricStore.instance : props.metric;
const data = _metric.data;
const period = dashboardStore.period;
const drillDownPeriod = dashboardStore.drillDownPeriod;
@ -65,9 +65,9 @@ function WidgetChart(props: Props) {
const [compData, setCompData] = useState<any>(null);
const [enabledRows, setEnabledRows] = useState([]);
const isTableWidget =
metric.metricType === 'table' && metric.viewType === 'table';
_metric.metricType === 'table' && _metric.viewType === 'table';
const isPieChart =
metric.metricType === 'table' && metric.viewType === 'pieChart';
_metric.metricType === 'table' && _metric.viewType === 'pieChart';
useEffect(() => {
return () => {
@ -147,17 +147,17 @@ function WidgetChart(props: Props) {
);
const loadPage = () => {
if (!inView) return;
if (prevMetricRef.current && prevMetricRef.current.name !== metric.name) {
prevMetricRef.current = metric;
if (prevMetricRef.current && prevMetricRef.current.name !== _metric.name) {
prevMetricRef.current = _metric;
return;
}
prevMetricRef.current = metric;
prevMetricRef.current = _metric;
const timestmaps = drillDownPeriod.toTimestamps();
const payload = isSaved
? { ...metricParams }
: { ...params, ...timestmaps, ...metric.toJson() };
: { ...params, ...timestmaps, ..._metric.toJson() };
debounceRequest(
metric,
_metric,
payload,
isSaved,
!isSaved ? drillDownPeriod : period
@ -172,11 +172,11 @@ function WidgetChart(props: Props) {
const payload = {
...params,
...timestamps,
...metric.toJson(),
..._metric.toJson(),
viewType: 'lineChart',
};
fetchMetricChartData(
metric,
_metric,
payload,
isSaved,
dashboardStore.comparisonPeriod,
@ -198,19 +198,19 @@ function WidgetChart(props: Props) {
period,
depsString,
dashboardStore.selectedDensity,
metric.metricType,
metric.metricOf,
metric.metricValue,
metric.startType,
metric.metricFormat,
_metric.metricType,
_metric.metricOf,
_metric.metricValue,
_metric.startType,
_metric.metricFormat,
inView,
]);
useEffect(loadPage, [_metric.page]);
const renderChart = React.useCallback(() => {
const { metricType, metricOf } = metric;
const viewType = metric.viewType;
const metricWithData = { ...metric, data };
const { metricType, metricOf } = _metric;
const viewType = _metric.viewType;
const metricWithData = { ..._metric, data };
if (metricType === FUNNEL) {
if (viewType === 'table') {
@ -250,7 +250,7 @@ function WidgetChart(props: Props) {
return (
<FunnelWidget
metric={metric}
metric={_metric}
data={data}
compData={compData}
isWidget={isSaved || isTemplate}
@ -260,7 +260,7 @@ function WidgetChart(props: Props) {
if (metricType === 'predefined' || metricType === ERRORS) {
const defaultMetric =
metric.data.chart && metric.data.chart.length === 0
_metric.data.chart && _metric.data.chart.length === 0
? metricWithData
: metric;
return (
@ -268,7 +268,7 @@ function WidgetChart(props: Props) {
isTemplate={isTemplate}
metric={defaultMetric}
data={data}
predefinedKey={metric.metricOf}
predefinedKey={_metric.metricOf}
/>
);
}
@ -288,7 +288,7 @@ function WidgetChart(props: Props) {
params={params}
onClick={onChartClick}
label={
metric.metricOf === 'sessionCount'
_metric.metricOf === 'sessionCount'
? 'Number of Sessions'
: 'Number of Users'
}
@ -304,7 +304,7 @@ function WidgetChart(props: Props) {
colors={colors}
onClick={onChartClick}
label={
metric.metricOf === 'sessionCount'
_metric.metricOf === 'sessionCount'
? 'Number of Sessions'
: 'Number of Users'
}
@ -321,7 +321,7 @@ function WidgetChart(props: Props) {
colors={colors}
onClick={onChartClick}
label={
metric.metricOf === 'sessionCount'
_metric.metricOf === 'sessionCount'
? 'Number of Sessions'
: 'Number of Users'
}
@ -338,7 +338,7 @@ function WidgetChart(props: Props) {
colors={colors}
onClick={onChartClick}
label={
metric.metricOf === 'sessionCount'
_metric.metricOf === 'sessionCount'
? 'Number of Sessions'
: 'Number of Users'
}
@ -349,12 +349,12 @@ function WidgetChart(props: Props) {
return (
<CustomMetricPieChart
inGrid={!props.isPreview}
metric={metric}
metric={_metric}
data={chartData}
colors={colors}
onClick={onChartClick}
label={
metric.metricOf === 'sessionCount'
_metric.metricOf === 'sessionCount'
? 'Number of Sessions'
: 'Number of Users'
}
@ -369,7 +369,7 @@ function WidgetChart(props: Props) {
colors={colors}
params={params}
label={
metric.metricOf === 'sessionCount'
_metric.metricOf === 'sessionCount'
? 'Number of Sessions'
: 'Number of Users'
}
@ -399,7 +399,7 @@ function WidgetChart(props: Props) {
colors={colors}
onClick={onChartClick}
label={
metric.metricOf === 'sessionCount'
_metric.metricOf === 'sessionCount'
? 'Number of Sessions'
: 'Number of Users'
}
@ -412,7 +412,7 @@ function WidgetChart(props: Props) {
if (metricOf === FilterKey.SESSIONS) {
return (
<CustomMetricTableSessions
metric={metric}
metric={_metric}
data={data}
isTemplate={isTemplate}
isEdit={!isSaved && !isTemplate}
@ -422,7 +422,7 @@ function WidgetChart(props: Props) {
if (metricOf === FilterKey.ERRORS) {
return (
<CustomMetricTableErrors
metric={metric}
metric={_metric}
data={data}
// isTemplate={isTemplate}
isEdit={!isSaved && !isTemplate}
@ -432,7 +432,7 @@ function WidgetChart(props: Props) {
if (viewType === TABLE) {
return (
<SessionsBy
metric={metric}
metric={_metric}
data={data}
onClick={onChartClick}
isTemplate={isTemplate}
@ -442,7 +442,7 @@ function WidgetChart(props: Props) {
}
if (metricType === HEATMAP) {
if (!props.isPreview) {
return metric.thumbnail ? (
return _metric.thumbnail ? (
<div
style={{
height: '229px',
@ -450,7 +450,7 @@ function WidgetChart(props: Props) {
marginBottom: '10px',
}}
>
<img src={metric.thumbnail} alt="clickmap thumbnail" />
<img src={_metric.thumbnail} alt="clickmap thumbnail" />
</div>
) : (
<div
@ -497,7 +497,7 @@ function WidgetChart(props: Props) {
}
}
return <div>Unknown metric type</div>;
}, [data, compData, enabledRows, metric]);
}, [data, compData, enabledRows, _metric]);
return (
<div ref={ref}>
@ -506,19 +506,19 @@ function WidgetChart(props: Props) {
style={{
minHeight: props.isPreview ? undefined : 240,
paddingTop:
props.isPreview && metric.metricType === TIMESERIES
props.isPreview && _metric.metricType === TIMESERIES
? '1.5rem'
: 0,
}}
>
{renderChart()}
{props.isPreview && metric.metricType === TIMESERIES ? (
{props.isPreview && _metric.metricType === TIMESERIES ? (
<WidgetDatatable
defaultOpen={metric.viewType === 'table'}
defaultOpen={_metric.viewType === 'table'}
data={data}
enabledRows={enabledRows}
setEnabledRows={setEnabledRows}
metric={metric}
metric={_metric}
/>
) : null}
</div>

View file

@ -130,11 +130,13 @@ export default class MetricStore {
merge(obj: any, updateChangeFlag: boolean = true) {
const type = obj.metricType;
// handle metricType change
if (obj.hasOwnProperty('metricType') && type !== this.instance.metricType) {
this.instance.series.forEach((s: any, i: number) => {
this.instance.series[i].filter.eventsOrderSupport = ['then', 'or', 'and']
})
if (type === HEATMAP && 'series' in obj) {
delete obj['series']
}
this.changeType(type);
}

View file

@ -100,6 +100,7 @@ export default class Widget {
sessions: [],
issues: [],
total: 0,
values: [],
chart: [],
namesMap: {},
avg: 0,
@ -261,7 +262,6 @@ export default class Widget {
}
update(data: any) {
console.log(this.data, data.data)
runInAction(() => {
Object.assign(this, data);
});
@ -295,6 +295,7 @@ export default class Widget {
}
setData(data: { timestamp: number, [seriesName: string]: number}[], period: any, isComparison?: boolean) {
if (!data) return;
const _data: any = {};
if (isComparison && this.metricType === TIMESERIES) {
data.forEach((point, i) => {
@ -306,6 +307,10 @@ export default class Widget {
})
}
if (this.metricType === HEATMAP) {
return;
}
if (this.metricType === USER_PATH) {
const _data = processData(data);
Object.assign(this.data, _data);
@ -342,10 +347,9 @@ export default class Widget {
return unique;
}, []);
} else {
const updatedData: any = data; // we don't use total anymore this.calculateTotalSeries(data);
_data['chart'] = getChartFormatter(period)(updatedData);
_data['namesMap'] = Array.isArray(updatedData)
? updatedData
_data['chart'] = getChartFormatter(period)(data);
_data['namesMap'] = Array.isArray(data)
? data
.map((i) => Object.keys(i))
.flat()
.filter((i) => i !== 'time' && i !== 'timestamp')
@ -359,8 +363,11 @@ export default class Widget {
}
}
if (!isComparison) {
Object.assign(this.data, _data);
runInAction(() => {
Object.assign(this.data, _data);
})
}
return _data;
}