226 lines
9 KiB
TypeScript
226 lines
9 KiB
TypeScript
import React from 'react';
|
|
import { metricTypes, metricOf, issueOptions } from 'App/constants/filterOptions';
|
|
import { FilterKey } from 'Types/filter/filterType';
|
|
import { useStore } from 'App/mstore';
|
|
import { useObserver } from 'mobx-react-lite';
|
|
import { Button, Icon, SegmentSelection } from 'UI'
|
|
import FilterSeries from '../FilterSeries';
|
|
import { confirm, Popup } from 'UI';
|
|
import Select from 'Shared/Select'
|
|
import { withSiteId, dashboardMetricDetails, metricDetails } from 'App/routes'
|
|
|
|
interface Props {
|
|
history: any;
|
|
match: any;
|
|
onDelete: () => void;
|
|
}
|
|
|
|
const metricIcons = {
|
|
timeseries: 'graph-up',
|
|
table: 'table',
|
|
funnel: 'funnel',
|
|
}
|
|
|
|
function WidgetForm(props: Props) {
|
|
|
|
const { history, match: { params: { siteId, dashboardId } } } = props;
|
|
const { metricStore, dashboardStore } = useStore();
|
|
const dashboards = dashboardStore.dashboards;
|
|
const isSaving = useObserver(() => metricStore.isSaving);
|
|
const metric: any = useObserver(() => metricStore.instance)
|
|
|
|
const timeseriesOptions = metricOf.filter(i => i.type === 'timeseries');
|
|
const tableOptions = metricOf.filter(i => i.type === 'table');
|
|
const isTable = metric.metricType === 'table';
|
|
const isFunnel = metric.metricType === 'funnel';
|
|
const canAddToDashboard = metric.exists() && dashboards.length > 0;
|
|
const canAddSeries = metric.series.length < 3;
|
|
const eventsLength = useObserver(() => metric.series[0].filter.filters.filter((i: any) => i.isEvent).length)
|
|
const cannotSaveFunnel = isFunnel && (!metric.series[0] || eventsLength <= 1);
|
|
|
|
const writeOption = ({ value, name }: any) => {
|
|
value = Array.isArray(value) ? value : value.value
|
|
const obj: any = { [ name ]: value };
|
|
|
|
if (name === 'metricValue') {
|
|
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'] = [{ value: 'all', label: 'All' }];
|
|
// }
|
|
}
|
|
|
|
if (name === 'metricType') {
|
|
if (value === 'timeseries') {
|
|
obj['metricOf'] = timeseriesOptions[0].value;
|
|
obj['viewType'] = 'lineChart';
|
|
} else if (value === 'table') {
|
|
obj['metricOf'] = tableOptions[0].value;
|
|
obj['viewType'] = 'table';
|
|
}
|
|
}
|
|
|
|
metricStore.merge(obj);
|
|
};
|
|
|
|
const onSelect = (_: any, option: Record<string, any>) => writeOption({ value: { value: option.value }, name: option.name})
|
|
|
|
const onSave = () => {
|
|
const wasCreating = !metric.exists()
|
|
metricStore.save(metric, dashboardId)
|
|
.then((metric: any) => {
|
|
if (wasCreating) {
|
|
if (parseInt(dashboardId) > 0) {
|
|
history.replace(withSiteId(dashboardMetricDetails(dashboardId, metric.metricId), siteId));
|
|
} else {
|
|
history.replace(withSiteId(metricDetails(metric.metricId), siteId));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
const onDelete = async () => {
|
|
if (await confirm({
|
|
header: 'Confirm',
|
|
confirmButton: 'Yes, delete',
|
|
confirmation: `Are you sure you want to permanently delete this metric?`
|
|
})) {
|
|
metricStore.delete(metric).then(props.onDelete);
|
|
}
|
|
}
|
|
|
|
return useObserver(() => (
|
|
<div className="p-6">
|
|
<div className="form-group">
|
|
<label className="font-medium">Metric Type</label>
|
|
<div className="flex items-center">
|
|
<SegmentSelection
|
|
icons
|
|
outline
|
|
name="metricType"
|
|
className="my-3"
|
|
onSelect={ onSelect }
|
|
value={metricTypes.find((i) => i.value === metric.metricType) || metricTypes[0]}
|
|
// @ts-ignore
|
|
list={metricTypes.map((i) => ({ value: i.value, name: i.label, icon: metricIcons[i.value] }))}
|
|
/>
|
|
|
|
{metric.metricType === 'timeseries' && (
|
|
<>
|
|
<span className="mx-3">of</span>
|
|
<Select
|
|
name="metricOf"
|
|
options={timeseriesOptions}
|
|
defaultValue={metric.metricOf}
|
|
onChange={ writeOption }
|
|
/>
|
|
</>
|
|
)}
|
|
|
|
{metric.metricType === 'table' && (
|
|
<>
|
|
<span className="mx-3">of</span>
|
|
<Select
|
|
name="metricOf"
|
|
options={tableOptions}
|
|
defaultValue={metric.metricOf}
|
|
onChange={ writeOption }
|
|
/>
|
|
</>
|
|
)}
|
|
|
|
{metric.metricOf === FilterKey.ISSUE && (
|
|
<>
|
|
<span className="mx-3">issue type</span>
|
|
<Select
|
|
name="metricValue"
|
|
options={issueOptions}
|
|
value={metric.metricValue}
|
|
onChange={ writeOption }
|
|
isMulti={true}
|
|
placeholder="All Issues"
|
|
/>
|
|
</>
|
|
)}
|
|
|
|
{metric.metricType === 'table' && !(metric.metricOf === FilterKey.ERRORS || metric.metricOf === FilterKey.SESSIONS) && (
|
|
<>
|
|
<span className="mx-3">showing</span>
|
|
<Select
|
|
name="metricFormat"
|
|
options={[
|
|
{ value: 'sessionCount', label: 'Session Count' },
|
|
]}
|
|
defaultValue={ metric.metricFormat }
|
|
onChange={ writeOption }
|
|
/>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<div className="flex items-center font-medium py-2">
|
|
{`${(isTable || isFunnel) ? 'Filter by' : 'Chart Series'}`}
|
|
{!isTable && !isFunnel && (
|
|
<Button
|
|
className="ml-2"
|
|
variant="text-primary"
|
|
onClick={() => metric.addSeries()}
|
|
disabled={!canAddSeries}
|
|
>Add Series</Button>
|
|
)}
|
|
</div>
|
|
|
|
{metric.series.length > 0 && metric.series.slice(0, (isTable || isFunnel) ? 1 : metric.series.length).map((series: any, index: number) => (
|
|
<div className="mb-2">
|
|
<FilterSeries
|
|
observeChanges={() => metric.updateKey('hasChanged', true)}
|
|
hideHeader={ isTable }
|
|
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.'
|
|
}
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="form-groups flex items-center justify-between">
|
|
<Popup
|
|
content="Cannot save funnel metric without at least 2 events"
|
|
disabled={!cannotSaveFunnel}
|
|
>
|
|
<Button
|
|
variant="primary"
|
|
onClick={onSave}
|
|
disabled={isSaving || cannotSaveFunnel}
|
|
>
|
|
{metric.exists() ? 'Update' : 'Create'}
|
|
</Button>
|
|
</Popup>
|
|
<div className="flex items-center">
|
|
{metric.exists() && (
|
|
<Button variant="text-primary" onClick={onDelete}>
|
|
<Icon name="trash" size="14" className="mr-2" color="teal"/>
|
|
Delete
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
));
|
|
}
|
|
|
|
export default WidgetForm;
|