Changes in filters will be updated.
}
+ { savedSearch.exists() && Changes in filters will be updated.
}
@@ -82,8 +100,9 @@ function SaveSearchModal(props: Props) {
primary
onClick={ onSave }
loading={ loading }
+ disabled={!savedSearch.validate()}
>
- { savedSearch ? 'Update' : 'Create' }
+ { savedSearch.exists() ? 'Update' : 'Create' }
diff --git a/frontend/app/components/shared/SavedSearch/SavedSearch.tsx b/frontend/app/components/shared/SavedSearch/SavedSearch.tsx
index cc156474c..2317fba65 100644
--- a/frontend/app/components/shared/SavedSearch/SavedSearch.tsx
+++ b/frontend/app/components/shared/SavedSearch/SavedSearch.tsx
@@ -36,7 +36,7 @@ function SavedSearch(props) {
{`Search Saved (${list.size})`}
- { savedSearch && (
+ { savedSearch.exists() && (
Viewing:
diff --git a/frontend/app/duck/customMetrics.js b/frontend/app/duck/customMetrics.js
index e486e75b0..8d7b49deb 100644
--- a/frontend/app/duck/customMetrics.js
+++ b/frontend/app/duck/customMetrics.js
@@ -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,
+ });
}
\ No newline at end of file
diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js
index 027a03b88..e8472d868 100644
--- a/frontend/app/duck/search.js
+++ b/frontend/app/duck/search.js
@@ -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)));
-}
\ No newline at end of file
+}
+
+export const editSavedSearch = instance => {
+ return {
+ type: EDIT_SAVED_SEARCH,
+ instance,
+ }
+};
\ No newline at end of file
diff --git a/frontend/app/types/customMetric.js b/frontend/app/types/customMetric.js
index f80c045fd..0218b8f93 100644
--- a/frontend/app/types/customMetric.js
+++ b/frontend/app/types/customMetric.js
@@ -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;
});
diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js
index 74b739918..91ebb92a7 100644
--- a/frontend/app/types/filter/newFilter.js
+++ b/frontend/app/types/filter/newFilter.js
@@ -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,
diff --git a/frontend/app/types/filter/savedFilter.js b/frontend/app/types/filter/savedFilter.js
index 83518f20d..3d9947fc6 100644
--- a/frontend/app/types/filter/savedFilter.js
+++ b/frontend/app/types/filter/savedFilter.js
@@ -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]}))