feat(ui) - funnels - issue details
This commit is contained in:
parent
b26f2e87bf
commit
88adec9e84
13 changed files with 107 additions and 82 deletions
|
|
@ -9,8 +9,6 @@ import { numberWithCommas } from 'App/utils';
|
|||
interface Props {
|
||||
metric: any,
|
||||
data: any;
|
||||
params: any;
|
||||
// seriesMap: any;
|
||||
colors: any;
|
||||
onClick?: (filters) => void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './CustomMetricTableSessions';
|
||||
|
|
@ -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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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' },
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 || {});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue