feat(ui) - custom metrics fixes
This commit is contained in:
parent
be0efc443c
commit
ec2eff546a
14 changed files with 216 additions and 163 deletions
|
|
@ -7,8 +7,7 @@ import { LineChart, Line, Legend } from 'recharts';
|
|||
import { LAST_24_HOURS, LAST_30_MINUTES, YESTERDAY, LAST_7_DAYS } from 'Types/app/period';
|
||||
import stl from './CustomMetricWidget.css';
|
||||
import { getChartFormatter, getStartAndEndTimestampsByDensity } from 'Types/dashboard/helper';
|
||||
import { edit, remove, setAlertMetricId, setActiveWidget, updateActiveState } from 'Duck/customMetrics';
|
||||
import { confirm } from 'UI/Confirmation';
|
||||
import { init, edit, remove, setAlertMetricId, setActiveWidget, updateActiveState } from 'Duck/customMetrics';
|
||||
import APIClient from 'App/api_client';
|
||||
import { setShowAlerts } from 'Duck/dashboard';
|
||||
|
||||
|
|
@ -35,6 +34,7 @@ interface Props {
|
|||
setShowAlerts: (showAlerts) => void;
|
||||
setAlertMetricId: (id) => void;
|
||||
onAlertClick: (e) => void;
|
||||
init: (metric) => void;
|
||||
edit: (setDefault?) => void;
|
||||
setActiveWidget: (widget) => void;
|
||||
updateActiveState: (metricId, state) => void;
|
||||
|
|
@ -74,16 +74,6 @@ function CustomMetricWidget(props: Props) {
|
|||
}).finally(() => setLoading(false));
|
||||
}, [period])
|
||||
|
||||
const deleteHandler = async () => {
|
||||
if (await confirm({
|
||||
header: 'Custom Metric',
|
||||
confirmButton: 'Delete',
|
||||
confirmation: `Are you sure you want to delete ${metric.name}`
|
||||
})) {
|
||||
props.remove(metric.metricId)
|
||||
}
|
||||
}
|
||||
|
||||
const clickHandler = (event, index) => {
|
||||
const timestamp = event.activePayload[0].payload.timestamp;
|
||||
const { startTimestamp, endTimestamp } = getStartAndEndTimestampsByDensity(timestamp, period.start, period.end, params.density);
|
||||
|
|
@ -100,7 +90,7 @@ function CustomMetricWidget(props: Props) {
|
|||
<div className="font-medium">{metric.name}</div>
|
||||
<div className="ml-auto flex items-center">
|
||||
<WidgetIcon className="cursor-pointer mr-6" icon="bell-plus" tooltip="Set Alert" onClick={props.onAlertClick} />
|
||||
<WidgetIcon className="cursor-pointer mr-6" icon="pencil" tooltip="Edit Metric" onClick={() => props.edit(metric)} />
|
||||
<WidgetIcon className="cursor-pointer mr-6" icon="pencil" tooltip="Edit Metric" onClick={() => props.init(metric)} />
|
||||
<WidgetIcon className="cursor-pointer" icon="close" tooltip="Hide Metric" onClick={() => updateActiveState(metric.metricId, false)} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -164,7 +154,15 @@ function CustomMetricWidget(props: Props) {
|
|||
|
||||
export default connect(state => ({
|
||||
period: state.getIn(['dashboard', 'period']),
|
||||
}), { remove, setShowAlerts, setAlertMetricId, edit, setActiveWidget, updateActiveState })(CustomMetricWidget);
|
||||
}), {
|
||||
remove,
|
||||
setShowAlerts,
|
||||
setAlertMetricId,
|
||||
edit,
|
||||
setActiveWidget,
|
||||
updateActiveState,
|
||||
init,
|
||||
})(CustomMetricWidget);
|
||||
|
||||
|
||||
const WidgetIcon = ({ className = '', tooltip = '', icon, onClick }) => (
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Loader, NoContent, Icon } from 'UI';
|
|||
import { widgetHOC, Styles } from '../../common';
|
||||
import { ResponsiveContainer, AreaChart, XAxis, YAxis, CartesianGrid, Area, Tooltip } from 'recharts';
|
||||
import { LineChart, Line, Legend } from 'recharts';
|
||||
import { LAST_24_HOURS, LAST_30_MINUTES, YESTERDAY, LAST_7_DAYS } from 'Types/app/period';
|
||||
import Period, { LAST_24_HOURS, LAST_30_MINUTES, YESTERDAY, LAST_7_DAYS } from 'Types/app/period';
|
||||
import stl from './CustomMetricWidgetPreview.css';
|
||||
import { getChartFormatter } from 'Types/dashboard/helper';
|
||||
import { remove } from 'Duck/customMetrics';
|
||||
|
|
@ -25,26 +25,23 @@ const customParams = rangeName => {
|
|||
return params
|
||||
}
|
||||
|
||||
interface Period {
|
||||
rangeName: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
metric: any;
|
||||
// loading?: boolean;
|
||||
data?: any;
|
||||
showSync?: boolean;
|
||||
compare?: boolean;
|
||||
period?: Period;
|
||||
// period?: any;
|
||||
onClickEdit?: (e) => void;
|
||||
remove: (id) => void;
|
||||
edit: (metric) => void;
|
||||
}
|
||||
function CustomMetricWidget(props: Props) {
|
||||
const { metric, showSync, compare, period = { rangeName: LAST_24_HOURS} } = props;
|
||||
const { metric, showSync, compare } = 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 = compare ? Styles.compareColors : Styles.colors;
|
||||
const params = customParams(period.rangeName)
|
||||
|
|
@ -70,13 +67,15 @@ function CustomMetricWidget(props: Props) {
|
|||
}, []);
|
||||
|
||||
setSeriesMap(namesMap);
|
||||
|
||||
setData(getChartFormatter(period)(data));
|
||||
}
|
||||
}).finally(() => setLoading(false));
|
||||
}, [metric])
|
||||
|
||||
const onDateChange = (changedDates) => {
|
||||
props.edit({ ...changedDates });
|
||||
setPeriod({ ...changedDates, rangeName: changedDates.rangeValue })
|
||||
props.edit({ ...changedDates, rangeName: changedDates.rangeValue });
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -85,7 +84,7 @@ function CustomMetricWidget(props: Props) {
|
|||
<div className="mr-auto font-medium">Preview</div>
|
||||
<div>
|
||||
<DateRange
|
||||
rangeValue={metric.rangeValue}
|
||||
rangeValue={metric.rangeName}
|
||||
startDate={metric.startDate}
|
||||
endDate={metric.endDate}
|
||||
onDateChange={onDateChange}
|
||||
|
|
@ -154,4 +153,6 @@ function CustomMetricWidget(props: Props) {
|
|||
);
|
||||
}
|
||||
|
||||
export default connect(null, { remove, edit })(CustomMetricWidget);
|
||||
export default connect(state => ({
|
||||
// period: state.getIn(['dashboard', 'period']),
|
||||
}), { remove, edit })(CustomMetricWidget);
|
||||
|
|
@ -72,7 +72,7 @@ export default class FunnelSaveModal extends React.PureComponent {
|
|||
/>
|
||||
</Form.Field>
|
||||
|
||||
<Form.Field>
|
||||
<Form.Field>
|
||||
<div className="flex items-center">
|
||||
<Checkbox
|
||||
name="isPublic"
|
||||
|
|
@ -86,7 +86,7 @@ export default class FunnelSaveModal extends React.PureComponent {
|
|||
<Icon name="user-friends" size="16" />
|
||||
<span className="ml-2"> Team Funnel</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form.Field>
|
||||
</Form>
|
||||
</Modal.Content>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { Form, SegmentSelection, Button, IconButton } from 'UI';
|
||||
import FilterSeries from '../FilterSeries';
|
||||
import { connect } from 'react-redux';
|
||||
import { edit as editMetric, save, addSeries, remove } from 'Duck/customMetrics';
|
||||
import { edit as editMetric, save, addSeries, removeSeries, remove } from 'Duck/customMetrics';
|
||||
import CustomMetricWidgetPreview from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview';
|
||||
import { confirm } from 'UI/Confirmation';
|
||||
import { toast } from 'react-toastify';
|
||||
|
|
@ -16,6 +16,7 @@ interface Props {
|
|||
addSeries: (series?) => void;
|
||||
onClose: () => void;
|
||||
remove: (id) => Promise<void>;
|
||||
removeSeries: (seriesIndex) => void;
|
||||
}
|
||||
|
||||
function CustomMetricForm(props: Props) {
|
||||
|
|
@ -23,30 +24,10 @@ function CustomMetricForm(props: Props) {
|
|||
|
||||
const addSeries = () => {
|
||||
props.addSeries();
|
||||
const newSeries = {
|
||||
name: `Series ${metric.series.size + 1}`,
|
||||
type: '',
|
||||
// series: [],
|
||||
filter: {
|
||||
type: '',
|
||||
value: '',
|
||||
filters: [],
|
||||
},
|
||||
};
|
||||
props.editMetric({
|
||||
...metric,
|
||||
series: metric.series.concat(newSeries),
|
||||
});
|
||||
}
|
||||
|
||||
const removeSeries = (index) => {
|
||||
const newSeries = metric.series.filter((_series, i) => {
|
||||
return i !== index;
|
||||
});
|
||||
props.editMetric({
|
||||
...metric,
|
||||
series: newSeries,
|
||||
});
|
||||
props.removeSeries(index);
|
||||
}
|
||||
|
||||
const write = ({ target: { value, name } }) => props.editMetric({ ...metric, [ name ]: value })
|
||||
|
|
@ -159,4 +140,4 @@ function CustomMetricForm(props: Props) {
|
|||
export default connect(state => ({
|
||||
metric: state.getIn(['customMetrics', 'instance']),
|
||||
loading: state.getIn(['customMetrics', 'saveRequest', 'loading']),
|
||||
}), { editMetric, save, addSeries, remove })(CustomMetricForm);
|
||||
}), { editMetric, save, addSeries, remove, removeSeries })(CustomMetricForm);
|
||||
|
|
@ -24,10 +24,10 @@ function CustomMetrics(props: Props) {
|
|||
</div>
|
||||
}
|
||||
isDisplayed={ !!metric }
|
||||
onClose={ () => props.init(null, false)}
|
||||
onClose={ () => props.init(null, true)}
|
||||
content={ (!!metric) && (
|
||||
<div style={{ backgroundColor: '#f6f6f6' }}>
|
||||
<CustomMetricForm metric={metric} onClose={() => props.init(null, false)} />
|
||||
<CustomMetricForm metric={metric} onClose={() => props.init(null, true)} />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
import React, { useState } from 'react';
|
||||
import FilterList from 'Shared/Filters/FilterList';
|
||||
import { edit, updateSeries } from 'Duck/customMetrics';
|
||||
import {
|
||||
edit,
|
||||
updateSeries,
|
||||
addSeriesFilterFilter,
|
||||
removeSeriesFilterFilter,
|
||||
editSeriesFilterFilter,
|
||||
editSeriesFilter,
|
||||
} from 'Duck/customMetrics';
|
||||
import { connect } from 'react-redux';
|
||||
import { IconButton, Icon } from 'UI';
|
||||
import FilterSelection from '../../Filters/FilterSelection';
|
||||
|
|
@ -14,6 +21,10 @@ interface Props {
|
|||
updateSeries: typeof updateSeries;
|
||||
onRemoveSeries: (seriesIndex) => void;
|
||||
canDelete?: boolean;
|
||||
addSeriesFilterFilter: typeof addSeriesFilterFilter;
|
||||
editSeriesFilterFilter: typeof editSeriesFilterFilter;
|
||||
editSeriesFilter: typeof editSeriesFilter;
|
||||
removeSeriesFilterFilter: typeof removeSeriesFilterFilter;
|
||||
}
|
||||
|
||||
function FilterSeries(props: Props) {
|
||||
|
|
@ -23,56 +34,20 @@ function FilterSeries(props: Props) {
|
|||
|
||||
const onAddFilter = (filter) => {
|
||||
filter.value = [""]
|
||||
const newFilters = series.filter.filters.concat(filter);
|
||||
props.updateSeries(seriesIndex, {
|
||||
...series,
|
||||
filter: {
|
||||
...series.filter,
|
||||
filters: newFilters,
|
||||
}
|
||||
});
|
||||
props.addSeriesFilterFilter(seriesIndex, filter);
|
||||
}
|
||||
|
||||
const onUpdateFilter = (filterIndex, filter) => {
|
||||
const newFilters = series.filter.filters.map((_filter, i) => {
|
||||
if (i === filterIndex) {
|
||||
return filter;
|
||||
} else {
|
||||
return _filter;
|
||||
}
|
||||
});
|
||||
|
||||
props.updateSeries(seriesIndex, {
|
||||
...series,
|
||||
filter: {
|
||||
...series.filter,
|
||||
filters: newFilters,
|
||||
}
|
||||
});
|
||||
props.editSeriesFilterFilter(seriesIndex, filterIndex, filter);
|
||||
}
|
||||
|
||||
const onChangeEventsOrder = (e, { name, value }) => {
|
||||
props.updateSeries(seriesIndex, {
|
||||
...series,
|
||||
filter: {
|
||||
...series.filter,
|
||||
eventsOrder: value,
|
||||
}
|
||||
});
|
||||
|
||||
props.editSeriesFilter(seriesIndex, { eventsOrder: value });
|
||||
}
|
||||
|
||||
const onRemoveFilter = (filterIndex) => {
|
||||
const newFilters = series.filter.filters.filter((_filter, i) => {
|
||||
return i !== filterIndex;
|
||||
});
|
||||
|
||||
props.updateSeries(seriesIndex, {
|
||||
...series,
|
||||
filter: {
|
||||
...series.filter,
|
||||
filters: newFilters,
|
||||
}
|
||||
});
|
||||
props.removeSeriesFilterFilter(seriesIndex, filterIndex);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -121,4 +96,11 @@ function FilterSeries(props: Props) {
|
|||
);
|
||||
}
|
||||
|
||||
export default connect(null, { edit, updateSeries })(FilterSeries);
|
||||
export default connect(null, {
|
||||
edit,
|
||||
updateSeries,
|
||||
addSeriesFilterFilter,
|
||||
editSeriesFilterFilter,
|
||||
editSeriesFilter,
|
||||
removeSeriesFilterFilter,
|
||||
})(FilterSeries);
|
||||
|
|
@ -14,16 +14,12 @@ function SaveFilterButton(props: Props) {
|
|||
const [showModal, setshowModal] = useState(false)
|
||||
return (
|
||||
<div>
|
||||
{ savedSearch ? (
|
||||
{ savedSearch.exists() ? (
|
||||
<IconButton className="mr-2" onClick={() => setshowModal(true)} primaryText label="UPDATE SEARCH" icon="zoom-in" />
|
||||
) : (
|
||||
<IconButton className="mr-2" onClick={() => setshowModal(true)} primaryText label="SAVE SEARCH" icon="zoom-in" />
|
||||
)}
|
||||
|
||||
<SaveSearchModal
|
||||
show={showModal}
|
||||
closeHandler={() => setshowModal(false)}
|
||||
/>
|
||||
{ showModal && ( <SaveSearchModal show={showModal} closeHandler={() => setshowModal(false)} /> )}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { edit, save, remove } from 'Duck/search';
|
||||
import { editSavedSearch as edit, save, remove } from 'Duck/search';
|
||||
import { Button, Modal, Form, Icon, Checkbox } from 'UI';
|
||||
import { confirm } from 'UI/Confirmation';
|
||||
import stl from './SaveSearchModal.css';
|
||||
|
|
@ -9,25 +9,25 @@ interface Props {
|
|||
filter: any;
|
||||
loading: boolean;
|
||||
edit: (filter: any) => void;
|
||||
save: (searchId, name, filter: any) => Promise<void>;
|
||||
save: (searchId) => Promise<void>;
|
||||
show: boolean;
|
||||
closeHandler: () => void;
|
||||
savedSearch: any;
|
||||
remove: (filterId: number) => Promise<void>;
|
||||
}
|
||||
function SaveSearchModal(props: Props) {
|
||||
const [name, setName] = useState(props.savedSearch ? props.savedSearch.name : '');
|
||||
const { savedSearch, filter, loading, show, closeHandler } = props;
|
||||
const [name, setName] = useState(savedSearch ? savedSearch.name : '');
|
||||
|
||||
const onNameChange = ({ target: { value } }) => {
|
||||
// props.edit({ name: value });
|
||||
setName(value);
|
||||
props.edit({ name: value });
|
||||
// setName(value);
|
||||
};
|
||||
|
||||
const onSave = () => {
|
||||
const { filter, closeHandler } = props;
|
||||
if (name.trim() === '') return;
|
||||
props.save(savedSearch ? savedSearch.searchId : null, name, filter).then(function() {
|
||||
// if (name.trim() === '') return;
|
||||
props.save(savedSearch.exists() ? savedSearch.searchId : null).then(function() {
|
||||
// this.props.fetchFunnelsList();
|
||||
closeHandler();
|
||||
});
|
||||
|
|
@ -37,13 +37,15 @@ function SaveSearchModal(props: Props) {
|
|||
if (await confirm({
|
||||
header: 'Confirm',
|
||||
confirmButton: 'Yes, Delete',
|
||||
confirmation: `Are you sure you want to permanently delete this alert?`
|
||||
confirmation: `Are you sure you want to permanently delete this Saved serch?`,
|
||||
})) {
|
||||
props.remove(savedSearch.searchId).then(() => {
|
||||
closeHandler();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const onChangeOption = (e, { checked, name }) => props.edit({ [ name ]: checked })
|
||||
|
||||
|
||||
return (
|
||||
|
|
@ -68,13 +70,29 @@ function SaveSearchModal(props: Props) {
|
|||
autoFocus={ true }
|
||||
// className={ stl.name }
|
||||
name="name"
|
||||
value={ name }
|
||||
value={ savedSearch.name }
|
||||
onChange={ onNameChange }
|
||||
placeholder="Title"
|
||||
/>
|
||||
</Form.Field>
|
||||
|
||||
<Form.Field>
|
||||
<div className="flex items-center">
|
||||
<Checkbox
|
||||
name="isPublic"
|
||||
className="font-medium mr-3"
|
||||
type="checkbox"
|
||||
checked={ savedSearch.isPublic }
|
||||
onClick={ onChangeOption }
|
||||
/>
|
||||
<div className="flex items-center cursor-pointer" onClick={ () => props.edit({ 'isPublic' : !savedSearch.isPublic }) }>
|
||||
<Icon name="user-friends" size="16" />
|
||||
<span className="ml-2"> Team Search</span>
|
||||
</div>
|
||||
</div>
|
||||
</Form.Field>
|
||||
</Form>
|
||||
{ savedSearch && <div className="mt-2">Changes in filters will be updated.</div> }
|
||||
{ savedSearch.exists() && <div className="mt-2">Changes in filters will be updated.</div> }
|
||||
</Modal.Content>
|
||||
<Modal.Actions className="flex items-center px-6">
|
||||
<div className="mr-auto">
|
||||
|
|
@ -82,8 +100,9 @@ function SaveSearchModal(props: Props) {
|
|||
primary
|
||||
onClick={ onSave }
|
||||
loading={ loading }
|
||||
disabled={!savedSearch.validate()}
|
||||
>
|
||||
{ savedSearch ? 'Update' : 'Create' }
|
||||
{ savedSearch.exists() ? 'Update' : 'Create' }
|
||||
</Button>
|
||||
<Button className={ stl.cancelButton } marginRight onClick={ closeHandler }>{ 'Cancel' }</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ function SavedSearch(props) {
|
|||
<span className="mr-2">{`Search Saved (${list.size})`}</span>
|
||||
<Icon name="ellipsis-v" color="teal" size="14" />
|
||||
</Button>
|
||||
{ savedSearch && (
|
||||
{ savedSearch.exists() && (
|
||||
<div className="flex items-center ml-2">
|
||||
<Icon name="search" size="14" />
|
||||
<span className="color-gray-medium px-1">Viewing:</span>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,15 @@ const FETCH_LIST = fetchListType(name);
|
|||
const FETCH_SESSION_LIST = fetchListType(`${name}/FETCH_SESSION_LIST`);
|
||||
const FETCH = fetchType(name);
|
||||
const SAVE = saveType(name);
|
||||
|
||||
const ADD_SERIES = `${name}/ADD_SERIES`;
|
||||
const REMOVE_SERIES = `${name}/REMOVE_SERIES`;
|
||||
|
||||
const ADD_SERIES_FILTER_FILTER = `${name}/ADD_SERIES_FILTER_FILTER`;
|
||||
const REMOVE_SERIES_FILTER_FILTER = `${name}/REMOVE_SERIES_FILTER_FILTER`;
|
||||
|
||||
const EDIT_SERIES_FILTER = `${name}/EDIT_SERIES_FILTER`;
|
||||
const EDIT_SERIES_FILTER_FILTER = `${name}/EDIT_SERIES_FILTER_FILTER`;
|
||||
const UPDATE_ACTIVE_STATE = saveType(`${name}/UPDATE_ACTIVE_STATE`);
|
||||
const EDIT = editType(name);
|
||||
const INIT = `${name}/INIT`;
|
||||
|
|
@ -51,21 +60,35 @@ const initialState = Map({
|
|||
// Metric - Series - [] - filters
|
||||
function reducer(state = initialState, action = {}) {
|
||||
switch (action.type) {
|
||||
case EDIT:
|
||||
const instance = state.get('instance')
|
||||
if (instance) {
|
||||
return state.mergeIn([ 'instance' ], action.instance);
|
||||
} else {
|
||||
return state.set('instance', action.instance);
|
||||
}
|
||||
// Custom Metric
|
||||
case INIT:
|
||||
return state.set('instance', action.instance);
|
||||
case EDIT:
|
||||
return state.mergeIn([ 'instance' ], action.instance);
|
||||
case ADD_SERIES:
|
||||
const series = new FilterSeries(action.series);
|
||||
return state.updateIn([ 'instance', 'series' ], list => list.push(series));
|
||||
case REMOVE_SERIES:
|
||||
return state.updateIn([ 'instance', 'series' ], list => list.delete(action.index));
|
||||
case UPDATE_SERIES:
|
||||
console.log('update series', action.series);
|
||||
return state.mergeIn(['instance', 'series', action.index], action.series);
|
||||
|
||||
// Custom Metric - Series - Filters
|
||||
case EDIT_SERIES_FILTER:
|
||||
return state.mergeIn(['instance', 'series', action.seriesIndex, 'filter'], action.filter);
|
||||
|
||||
// Custom Metric - Series - Filter - Filters
|
||||
case EDIT_SERIES_FILTER_FILTER:
|
||||
return state.updateIn([ 'instance', 'series', action.seriesIndex, 'filter', 'filters' ], filters => filters.set(action.filterIndex, action.filter));
|
||||
case ADD_SERIES_FILTER_FILTER:
|
||||
return state.updateIn([ 'instance', 'series', action.seriesIndex, 'filter', 'filters' ], filters => filters.push(action.filter));
|
||||
case REMOVE_SERIES_FILTER_FILTER:
|
||||
return state.updateIn([ 'instance', 'series', action.seriesIndex, 'filter', 'filters' ], filters => filters.delete(action.index));
|
||||
|
||||
|
||||
|
||||
case success(SAVE):
|
||||
return updateItemInList(updateInstance(state, action.data), action.data);
|
||||
// return state.mergeIn([ 'instance' ], action.data);
|
||||
return updateItemInList(updateInstance(state, action.data), action.data);
|
||||
case success(REMOVE):
|
||||
return state.update('list', list => list.filter(item => item.metricId !== action.id));
|
||||
case success(FETCH):
|
||||
|
|
@ -73,8 +96,6 @@ function reducer(state = initialState, action = {}) {
|
|||
case success(FETCH_LIST):
|
||||
const { data } = action;
|
||||
return state.set("list", List(data.map(CustomMetric)));
|
||||
// case success(UPDATE_ACTIVE_STATE):
|
||||
// return updateItemInList(updateInstance(state, action.data), action.data);
|
||||
case success(FETCH_SESSION_LIST):
|
||||
return state.set("sessionList", List(action.data.map(item => ({ ...item, sessions: item.sessions.map(Session) }))));
|
||||
case SET_ACTIVE_WIDGET:
|
||||
|
|
@ -131,27 +152,36 @@ export function setAlertMetricId(id) {
|
|||
};
|
||||
}
|
||||
|
||||
export const addSeries = (series) => (dispatch, getState) => {
|
||||
const instance = getState().getIn([ 'customMetrics', 'instance' ])
|
||||
const _series = series || new FilterSeries({
|
||||
name: 'New',
|
||||
export const addSeries = (series = null) => (dispatch, getState) => {
|
||||
const instance = getState().getIn([ 'customMetrics', 'instance' ]);
|
||||
const seriesIndex = instance.series.size;
|
||||
const newSeries = series || {
|
||||
name: `Series ${seriesIndex + 1}`,
|
||||
filter: new Filter({ filters: [], eventsOrder: 'and' }),
|
||||
});
|
||||
return dispatch({
|
||||
type: EDIT,
|
||||
instance: {
|
||||
series: instance.series.push(_series)
|
||||
},
|
||||
};
|
||||
|
||||
dispatch({
|
||||
type: ADD_SERIES,
|
||||
series: newSeries,
|
||||
});
|
||||
}
|
||||
|
||||
export const init = (instnace = null, setDefault = true) => (dispatch, getState) => {
|
||||
export const removeSeries = (index) => (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: INIT,
|
||||
instance: instnace ? instnace : (setDefault ? defaultInstance : null),
|
||||
type: REMOVE_SERIES,
|
||||
index,
|
||||
});
|
||||
}
|
||||
|
||||
export const init = (instance = null, forceNull = false) => (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: INIT,
|
||||
instance: forceNull ? null : (instance || defaultInstance),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const fetchSessionList = (params) => (dispatch, getState) => {
|
||||
dispatch({
|
||||
types: array(FETCH_SESSION_LIST),
|
||||
|
|
@ -160,7 +190,7 @@ export const fetchSessionList = (params) => (dispatch, getState) => {
|
|||
}
|
||||
|
||||
export const setActiveWidget = (widget) => (dispatch, getState) => {
|
||||
dispatch({
|
||||
return dispatch({
|
||||
type: SET_ACTIVE_WIDGET,
|
||||
widget,
|
||||
});
|
||||
|
|
@ -174,4 +204,37 @@ export const updateActiveState = (metricId, state) => (dispatch, getState) => {
|
|||
}).then(() => {
|
||||
dispatch(fetchList());
|
||||
});
|
||||
}
|
||||
|
||||
export const editSeriesFilter = (seriesIndex, filter) => (dispatch, getState) => {
|
||||
return dispatch({
|
||||
type: EDIT_SERIES_FILTER,
|
||||
seriesIndex,
|
||||
filter,
|
||||
});
|
||||
}
|
||||
|
||||
export const addSeriesFilterFilter = (seriesIndex, filter) => (dispatch, getState) => {
|
||||
return dispatch({
|
||||
type: ADD_SERIES_FILTER_FILTER,
|
||||
seriesIndex,
|
||||
filter,
|
||||
});
|
||||
}
|
||||
|
||||
export const removeSeriesFilterFilter = (seriesIndex, filterIndex) => (dispatch, getState) => {
|
||||
return dispatch({
|
||||
type: REMOVE_SERIES_FILTER_FILTER,
|
||||
seriesIndex,
|
||||
index: filterIndex,
|
||||
});
|
||||
}
|
||||
|
||||
export const editSeriesFilterFilter = (seriesIndex, filterIndex, filter) => (dispatch, getState) => {
|
||||
return dispatch({
|
||||
type: EDIT_SERIES_FILTER_FILTER,
|
||||
seriesIndex,
|
||||
filterIndex,
|
||||
filter,
|
||||
});
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ const FETCH_FILTER_SEARCH = fetchListType(`${name}/FILTER_SEARCH`);
|
|||
const FETCH = fetchType(name);
|
||||
const SAVE = saveType(name);
|
||||
const EDIT = editType(name);
|
||||
const EDIT_SAVED_SEARCH = editType(`${name}/SAVED_SEARCH`);
|
||||
const REMOVE = removeType(name);
|
||||
const ADD_FILTER = `${name}/ADD_FILTER`;
|
||||
const APPLY_SAVED_SEARCH = `${name}/APPLY_SAVED_SEARCH`;
|
||||
|
|
@ -41,7 +42,7 @@ const initialState = Map({
|
|||
list: List(),
|
||||
alertMetricId: null,
|
||||
instance: new Filter({ filters: [] }),
|
||||
savedSearch: null,
|
||||
savedSearch: new SavedFilter({}),
|
||||
filterSearchList: {},
|
||||
});
|
||||
|
||||
|
|
@ -76,6 +77,8 @@ function reducer(state = initialState, action = {}) {
|
|||
return state.set('filterSearchList', groupedList);
|
||||
case APPLY_SAVED_SEARCH:
|
||||
return state.set('savedSearch', action.filter);
|
||||
case EDIT_SAVED_SEARCH:
|
||||
return state.mergeIn([ 'savedSearch' ], action.instance);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
|
@ -150,16 +153,17 @@ export function fetch(id) {
|
|||
}
|
||||
}
|
||||
|
||||
export function save(id, name, instance) {
|
||||
instance = instance instanceof SavedFilter ? instance : new SavedFilter(instance);
|
||||
return {
|
||||
export const save = (id) => (dispatch, getState) => {
|
||||
// export function save(id) {
|
||||
const filter = getState().getIn([ 'search', 'instance']).toData();
|
||||
filter.filters = filter.filters.map(filterMap);
|
||||
|
||||
const instance = getState().getIn([ 'search', 'savedSearch']).toData();
|
||||
// instance = instance instanceof SavedFilter ? instance : new SavedFilter(instance);
|
||||
return dispatch({
|
||||
types: SAVE.array,
|
||||
call: client => client.post(!id ? '/saved_search' : `/saved_search/${id}`, {
|
||||
name: name,
|
||||
filter: instance.toSaveData(),
|
||||
}),
|
||||
instance,
|
||||
};
|
||||
call: client => client.post(!id ? '/saved_search' : `/saved_search/${id}`, { ...instance, filter })
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchList() {
|
||||
|
|
@ -185,7 +189,7 @@ export function fetchFilterSearch(params) {
|
|||
}
|
||||
|
||||
export const clearSearch = () => (dispatch, getState) => {
|
||||
dispatch(applySavedSearch(null));
|
||||
dispatch(applySavedSearch(new SavedFilter({})));
|
||||
dispatch(edit(new Filter({ filters: [] })));
|
||||
return dispatch({
|
||||
type: CLEAR_SEARCH,
|
||||
|
|
@ -197,4 +201,11 @@ export const addFilter = (filter) => (dispatch, getState) => {
|
|||
const instance = getState().getIn([ 'search', 'instance']);
|
||||
const filters = instance.filters.push(filter);
|
||||
return dispatch(edit(instance.set('filters', filters)));
|
||||
}
|
||||
}
|
||||
|
||||
export const editSavedSearch = instance => {
|
||||
return {
|
||||
type: EDIT_SAVED_SEARCH,
|
||||
instance,
|
||||
}
|
||||
};
|
||||
|
|
@ -2,6 +2,8 @@ import Record from 'Types/Record';
|
|||
import { List } from 'immutable';
|
||||
import Filter from 'Types/filter';
|
||||
import { validateName } from 'App/validate';
|
||||
import Period, { LAST_24_HOURS, LAST_30_MINUTES, YESTERDAY, LAST_7_DAYS } from 'Types/app/period';
|
||||
import { filterMap } from 'Duck/search';
|
||||
|
||||
export const FilterSeries = Record({
|
||||
seriesId: undefined,
|
||||
|
|
@ -31,6 +33,7 @@ export default Record({
|
|||
startDate: '',
|
||||
endDate: '',
|
||||
active: true,
|
||||
rangeName: LAST_7_DAYS,
|
||||
}, {
|
||||
idKey: 'metricId',
|
||||
methods: {
|
||||
|
|
@ -42,16 +45,9 @@ export default Record({
|
|||
const js = this.toJS();
|
||||
|
||||
js.series = js.series.map(series => {
|
||||
series.filter.filters = series.filter.filters.map(filter => {
|
||||
filter.type = filter.key
|
||||
delete filter.operatorOptions
|
||||
delete filter.icon
|
||||
delete filter.key
|
||||
delete filter._key
|
||||
return filter;
|
||||
});
|
||||
delete series._key
|
||||
delete series.key
|
||||
series.filter.filters = series.filter.filters.map(filterMap);
|
||||
// delete series._key
|
||||
// delete series.key
|
||||
return series;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -93,8 +93,9 @@ export default Record({
|
|||
options: [],
|
||||
}, {
|
||||
keyKey: "_key",
|
||||
fromJS: ({ value, type, ...filter }) => {
|
||||
const _filter = filtersMap[type]
|
||||
fromJS: ({ value, key, type, ...filter }) => {
|
||||
// const _filter = filtersMap[key] || filtersMap[type] || {};
|
||||
const _filter = filtersMap[type];
|
||||
return {
|
||||
...filter,
|
||||
..._filter,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import Record from 'Types/Record';
|
||||
import Filter from './filter';
|
||||
import { List } from 'immutable';
|
||||
import { notEmptyString, validateName } from 'App/validate';
|
||||
|
||||
export default Record({
|
||||
searchId: undefined,
|
||||
|
|
@ -10,10 +11,14 @@ export default Record({
|
|||
filter: Filter(),
|
||||
createdAt: undefined,
|
||||
count: 0,
|
||||
watchdogs: List()
|
||||
watchdogs: List(),
|
||||
isPublic: true,
|
||||
}, {
|
||||
idKey: 'searchId',
|
||||
methods: {
|
||||
validate() {
|
||||
return notEmptyString(this.name);
|
||||
},
|
||||
toData() {
|
||||
const js = this.toJS();
|
||||
js.filter.filters = js.filter.filters.map(f => ({...f, value: Array.isArray(f.value) ? f.value : [f.value]}))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue