feat(ui) - funnels - issue details

This commit is contained in:
Shekar Siri 2022-06-13 17:20:09 +02:00
parent b26f2e87bf
commit 88adec9e84
13 changed files with 107 additions and 82 deletions

View file

@ -9,8 +9,6 @@ import { numberWithCommas } from 'App/utils';
interface Props {
metric: any,
data: any;
params: any;
// seriesMap: any;
colors: any;
onClick?: (filters) => void;
}

View file

@ -0,0 +1,16 @@
import React from 'react';
interface Props {
data: any
metric?: any
isTemplate?: boolean;
}
function CustomMetricTableSessions(props: Props) {
return (
<div>
</div>
);
}
export default CustomMetricTableSessions;

View file

@ -0,0 +1 @@
export { default } from './CustomMetricTableSessions';

View file

@ -1,28 +1,24 @@
import React, { useEffect, useState, useRef } from 'react';
import { connect } from 'react-redux';
import { Loader, NoContent, SegmentSelection, Icon } from 'UI';
import { Loader, NoContent, SegmentSelection } from 'UI';
import { Styles } from '../../common';
// import { ResponsiveContainer, XAxis, YAxis, CartesianGrid, Tooltip, LineChart, Line, Legend } from 'recharts';
import Period, { LAST_24_HOURS, LAST_30_MINUTES, YESTERDAY, LAST_7_DAYS } from 'Types/app/period';
import Period from 'Types/app/period';
import stl from './CustomMetricWidgetPreview.module.css';
import { getChartFormatter } from 'Types/dashboard/helper';
import { remove } from 'Duck/customMetrics';
import DateRange from 'Shared/DateRange';
import { edit } from 'Duck/customMetrics';
import CustomMetriLineChart from '../CustomMetriLineChart';
import CustomMetricPercentage from '../CustomMetricPercentage';
import CustomMetricTable from '../CustomMetricTable';
import APIClient from 'App/api_client';
import CustomMetricPieChart from '../CustomMetricPieChart';
const customParams = rangeName => {
const customParams = (rangeName: string) => {
const params = { density: 70 }
if (rangeName === LAST_24_HOURS) params.density = 70
if (rangeName === LAST_30_MINUTES) params.density = 70
if (rangeName === YESTERDAY) params.density = 70
if (rangeName === LAST_7_DAYS) params.density = 70
// if (rangeName === LAST_24_HOURS) params.density = 70
// if (rangeName === LAST_30_MINUTES) params.density = 70
// if (rangeName === YESTERDAY) params.density = 70
// if (rangeName === LAST_7_DAYS) params.density = 70
return params
}
@ -30,23 +26,18 @@ const customParams = rangeName => {
interface Props {
metric: any;
data?: any;
showSync?: boolean;
// compare?: boolean;
onClickEdit?: (e) => void;
remove: (id) => void;
edit: (metric) => void;
}
function CustomMetricWidget(props: Props) {
const { metric, showSync } = props;
const { metric } = props;
const [loading, setLoading] = useState(false)
const [data, setData] = useState<any>({ chart: [{}] })
const [seriesMap, setSeriesMap] = useState<any>([]);
const [period, setPeriod] = useState(Period({ rangeName: metric.rangeName, startDate: metric.startDate, endDate: metric.endDate }));
const colors = Styles.customMetricColors;
const params = customParams(period.rangeName)
const gradientDef = Styles.gradientDef();
const metricParams = { ...params, metricId: metric.metricId, viewType: 'lineChart' }
const prevMetricRef = useRef<any>();
const isTimeSeries = metric.metricType === 'timeseries';
const isTable = metric.metricType === 'table';
@ -59,29 +50,6 @@ function CustomMetricWidget(props: Props) {
};
prevMetricRef.current = metric;
setLoading(true);
// fetch new data for the widget preview
// new APIClient()['post']('/custom_metrics/try', { ...metricParams, ...metric.toSaveData() })
// .then(response => response.json())
// .then(({ errors, data }) => {
// if (errors) {
// console.log('err', errors)
// } else {
// const namesMap = data
// .map(i => Object.keys(i))
// .flat()
// .filter(i => i !== 'time' && i !== 'timestamp')
// .reduce((unique: any, item: any) => {
// if (!unique.includes(item)) {
// unique.push(item);
// }
// return unique;
// }, []);
// setSeriesMap(namesMap);
// setData(getChartFormatter(period)(data));
// }
// }).finally(() => setLoading(false));
}, [metric])
const onDateChange = (changedDates) => {
@ -185,7 +153,6 @@ function CustomMetricWidget(props: Props) {
metric={metric}
data={data[0]}
colors={colors}
params={params}
/>
)}
</>

View file

@ -36,17 +36,22 @@ function WidgetForm(props: Props) {
// const write = ({ target: { value, name } }) => metricStore.merge({ [ name ]: value });
const writeOption = ({ value, name }: any) => {
value = value.value
value = Array.isArray(value) ? value : value.value
const obj: any = { [ name ]: value };
if (name === 'metricValue') {
obj['metricValue'] = [value];
obj['metricValue'] = value;
// handle issues (remove all when other option is selected)
if (Array.isArray(obj['metricValue']) && obj['metricValue'].length > 1) {
obj['metricValue'] = obj['metricValue'].filter(i => i.value !== 'all');
}
}
if (name === 'metricOf') {
if (value === FilterKey.ISSUE) {
obj['metricValue'] = ['all'];
}
// if (value === FilterKey.ISSUE) {
// obj['metricValue'] = [{ value: 'all', label: 'All' }];
// }
}
if (name === 'metricType') {
@ -71,7 +76,6 @@ function WidgetForm(props: Props) {
} else {
history.replace(withSiteId(metricDetails(metric.metricId), siteId));
}
}
});
}
@ -131,9 +135,11 @@ function WidgetForm(props: Props) {
<span className="mx-3">issue type</span>
<Select
name="metricValue"
options={_issueOptions}
defaultValue={metric.metricValue[0]}
options={issueOptions}
value={metric.metricValue}
onChange={ writeOption }
isMulti={true}
placeholder="All Issues"
/>
</>
)}

View file

@ -2,6 +2,7 @@ import React from 'react';
import Select, { components, DropdownIndicatorProps } from 'react-select';
import { Icon } from 'UI';
import colors from 'App/theme/colors';
const { ValueContainer } = components;
interface Props {
options: any[];
@ -84,6 +85,7 @@ export default function({ name = '', onChange, right = false, plain = false, opt
},
indicatorsContainer: (provided: any) => ({
...provided,
maxHeight: '34px',
padding: 0,
}),
valueContainer: (provided: any) => ({
@ -112,6 +114,7 @@ export default function({ name = '', onChange, right = false, plain = false, opt
components={{
IndicatorSeparator: () => null,
DropdownIndicator,
ValueContainer: CustomValueContainer,
...components,
}}
onChange={(value) => onChange({ name, value: value })}
@ -138,4 +141,22 @@ const DropdownIndicator = (
<Icon name="chevron-down" size="16" />
</components.DropdownIndicator>
);
};
};
const CustomValueContainer = ({ children, ...rest }: any) => {
const selectedCount = rest.getValue().length
const conditional = (selectedCount < 3)
let firstChild: any = []
if (!conditional) {
firstChild = [children[0].shift(), children[1]]
}
return (
<ValueContainer {...rest}>
{conditional ? children : firstChild}
{!conditional && ` and ${selectedCount - 1} others`}
</ValueContainer>
)
}

View file

@ -63,8 +63,8 @@ export const metricTypes = [
{ text: 'Timeseries', label: 'Timeseries', value: 'timeseries' },
{ text: 'Table', label: 'Table', value: 'table' },
{ label: 'Funnel', value: 'funnel' },
{ label: 'Errors', value: 'errors' },
{ label: 'Sessions', value: 'sessions' },
// { label: 'Errors', value: 'errors' },
// { label: 'Sessions', value: 'sessions' },
];
export const tableColumnName = {
@ -79,6 +79,7 @@ export const tableColumnName = {
export const metricOf = [
{ text: 'Session Count', label: 'Session Count', value: 'sessionCount', type: 'timeseries' },
{ text: 'Users', label: 'Users', value: FilterKey.USERID, type: 'table' },
{ text: 'Sessions', label: 'Sessions', value: FilterKey.SESSIONS, type: 'table' },
{ text: 'Issues', label: 'Issues', value: FilterKey.ISSUE, type: 'table' },
{ text: 'Browsers', label: 'Browsers', value: FilterKey.USER_BROWSER, type: 'table' },
{ text: 'Devices', label: 'Devices', value: FilterKey.USER_DEVICE, type: 'table' },

View file

@ -79,7 +79,7 @@ export default class DashboardStore implements IDashboardSotre {
currentWidget: Widget = new Widget()
widgetCategories: any[] = []
widgets: Widget[] = []
period: Period = Period({ rangeName: LAST_7_DAYS })
period: Period = Period({ rangeName: LAST_24_HOURS })
drillDownFilter: Filter = new Filter()
startTimestamp: number = 0
endTimestamp: number = 0
@ -131,7 +131,7 @@ export default class DashboardStore implements IDashboardSotre {
fetchMetricChartData: action
})
const drillDownPeriod = Period({ rangeName: LAST_7_DAYS }).toTimestamps();
const drillDownPeriod = Period({ rangeName: LAST_24_HOURS }).toTimestamps();
this.drillDownFilter.updateKey('startTimestamp', drillDownPeriod.startTimestamp)
this.drillDownFilter.updateKey('endTimestamp', drillDownPeriod.endTimestamp)
}

View file

@ -90,7 +90,6 @@ export default class MetricStore implements IMetricStore {
// State Actions
init(metric?: IWidget|null) {
console.log('metric', metric);
// const _metric = new Widget().fromJson(sampleJsonErrors)
// this.instance.update(metric || _metric)
@ -141,23 +140,26 @@ export default class MetricStore implements IMetricStore {
save(metric: IWidget, dashboardId?: string): Promise<any> {
const wasCreating = !metric.exists()
this.isSaving = true
return metricService.saveMetric(metric, dashboardId)
.then((metric: any) => {
const _metric = new Widget().fromJson(metric)
if (wasCreating) {
toast.success('Metric created successfully')
this.addToList(_metric)
this.instance = _metric
} else {
toast.success('Metric updated successfully')
this.updateInList(_metric)
}
return _metric
}).catch(() => {
toast.error('Error saving metric')
}).finally(() => {
this.isSaving = false
})
return new Promise((resolve, reject) => {
metricService.saveMetric(metric, dashboardId)
.then((metric: any) => {
const _metric = new Widget().fromJson(metric)
if (wasCreating) {
toast.success('Metric created successfully')
this.addToList(_metric)
this.instance = _metric
} else {
toast.success('Metric updated successfully')
this.updateInList(_metric)
}
resolve(_metric)
}).catch(() => {
toast.error('Error saving metric')
reject()
}).finally(() => {
this.isSaving = false
})
})
}
fetchList() {

View file

@ -1,10 +1,10 @@
import { makeAutoObservable, runInAction, observable, action, reaction, computed } from "mobx"
import { makeAutoObservable, runInAction, observable, action } from "mobx"
import FilterSeries from "./filterSeries";
import { DateTime } from 'luxon';
import { IFilter } from "./filter";
import { metricService } from "App/services";
import Session, { ISession } from "App/mstore/types/session";
import Session from "App/mstore/types/session";
import Funnelissue from 'App/mstore/types/funnelIssue';
import { issueOptions } from 'App/constants/filterOptions';
export interface IWidget {
metricId: any
@ -54,7 +54,8 @@ export default class Widget implements IWidget {
metricId: any = undefined
widgetId: any = undefined
name: string = "New Metric"
metricType: string = "timeseries"
// metricType: string = "timeseries"
metricType: string = "table"
metricOf: string = "sessionCount"
metricValue: string = ""
viewType: string = "lineChart"
@ -132,7 +133,7 @@ export default class Widget implements IWidget {
runInAction(() => {
this.metricId = json.metricId
this.widgetId = json.widgetId
this.metricValue = json.metricValue
this.metricValue = this.metricValueFromArray(json.metricValue)
this.metricOf = json.metricOf
this.metricType = json.metricType
this.metricFormat = json.metricFormat
@ -168,7 +169,7 @@ export default class Widget implements IWidget {
metricId: this.metricId,
widgetId: this.widgetId,
metricOf: this.metricOf,
metricValue: this.metricValue,
metricValue: this.metricValueToArray(this.metricValue),
metricType: this.metricType,
metricFormat: this.metricFormat,
viewType: this.viewType,
@ -238,4 +239,14 @@ export default class Widget implements IWidget {
})
})
}
private metricValueFromArray(metricValue: any) {
if (!Array.isArray(metricValue)) return metricValue;
return issueOptions.filter((i: any) => metricValue.includes(i.value))
}
private metricValueToArray(metricValue: any) {
if (!Array.isArray(metricValue)) return metricValue;
return metricValue.map((i: any) => i.value)
}
}

View file

@ -60,7 +60,7 @@ export default class MetricService implements IMetricService {
const url = isCreating ? '/metrics' : '/metrics/' + data[Widget.ID_KEY];
return this.client[method](url, data)
.then((response: { json: () => any; }) => response.json())
.then((response: { data: any; }) => response.data || {});
.then((response: { data: any; }) => response.data || {});
}
/**

View file

@ -12,7 +12,7 @@ import Event from './event';
// import CustomFilter from './customFilter';
import NewFilter from './newFilter';
const rangeValue = DATE_RANGE_VALUES.LAST_7_DAYS;
const rangeValue = DATE_RANGE_VALUES.LAST_24_HOURS;
const range = getDateRangeFromValue(rangeValue);
const startDate = range.start.unix() * 1000;
const endDate = range.end.unix() * 1000;

View file

@ -91,4 +91,6 @@ export enum FilterKey {
GRAPHQL_METHOD = "GRAPHQL_METHOD",
GRAPHQL_REQUEST_BODY = "GRAPHQL_REQUEST_BODY",
GRAPHQL_RESPONSE_BODY = "GRAPHQL_RESPONSE_BODY",
SESSIONS = 'SESSIONS'
}