change(ui): customMetrics cleanup
This commit is contained in:
parent
d4d836ad24
commit
f2e103ad08
8 changed files with 2 additions and 755 deletions
|
|
@ -1,17 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { IconButton } from 'UI';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { edit, init } from 'Duck/customMetrics';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
init: (instance?, setDefault?) => void;
|
|
||||||
}
|
|
||||||
function CustomMetrics(props: Props) {
|
|
||||||
return (
|
|
||||||
<div className="self-start">
|
|
||||||
<IconButton plain outline icon="plus" label="CREATE METRIC" onClick={() => props.init()} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(null, { edit, init })(CustomMetrics);
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
.wrapper {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
min-height: calc(100vh - 59px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown {
|
|
||||||
display: flex !important;
|
|
||||||
padding: 4px 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
color: $gray-darkest;
|
|
||||||
font-weight: 500;
|
|
||||||
&:hover {
|
|
||||||
background-color: $gray-light;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdownTrigger {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 3px;
|
|
||||||
&:hover {
|
|
||||||
background-color: $gray-light;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdownIcon {
|
|
||||||
margin-top: 2px;
|
|
||||||
margin-left: 3px;
|
|
||||||
}
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { SlideModal, NoContent, Dropdown, Icon, TimezoneDropdown, Loader } from 'UI';
|
|
||||||
import SessionItem from 'Shared/SessionItem';
|
|
||||||
import stl from './SessionListModal.module.css';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { fetchSessionList, setActiveWidget } from 'Duck/customMetrics';
|
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
|
||||||
interface Props {
|
|
||||||
loading: boolean;
|
|
||||||
list: any;
|
|
||||||
fetchSessionList: (params) => void;
|
|
||||||
activeWidget: any;
|
|
||||||
setActiveWidget: (widget) => void;
|
|
||||||
}
|
|
||||||
function SessionListModal(props: Props) {
|
|
||||||
const { activeWidget, loading, list } = props;
|
|
||||||
const [seriesOptions, setSeriesOptions] = useState([
|
|
||||||
{ text: 'All', value: 'all' },
|
|
||||||
]);
|
|
||||||
const [activeSeries, setActiveSeries] = useState('all');
|
|
||||||
useEffect(() => {
|
|
||||||
if (!activeWidget || !activeWidget.widget) return;
|
|
||||||
props.fetchSessionList({
|
|
||||||
metricId: activeWidget.widget.metricId,
|
|
||||||
startDate: activeWidget.startTimestamp,
|
|
||||||
endDate: activeWidget.endTimestamp,
|
|
||||||
filters: activeWidget.filters || [],
|
|
||||||
});
|
|
||||||
}, [activeWidget]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!list) return;
|
|
||||||
const seriesOptions = list.map(item => ({
|
|
||||||
text: item.seriesName,
|
|
||||||
value: item.seriesId,
|
|
||||||
}));
|
|
||||||
setSeriesOptions([
|
|
||||||
{ text: 'All', value: 'all' },
|
|
||||||
...seriesOptions,
|
|
||||||
]);
|
|
||||||
}, [list]);
|
|
||||||
|
|
||||||
const getListSessionsBySeries = (seriesId) => {
|
|
||||||
const arr: any = []
|
|
||||||
list.forEach(element => {
|
|
||||||
if (seriesId === 'all') {
|
|
||||||
const sessionIds = arr.map(i => i.sessionId);
|
|
||||||
arr.push(...element.sessions.filter(i => !sessionIds.includes(i.sessionId)));
|
|
||||||
} else {
|
|
||||||
if (element.seriesId === seriesId) {
|
|
||||||
arr.push(...element.sessions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const writeOption = (e, { name, value }) => setActiveSeries(value);
|
|
||||||
const filteredSessions = getListSessionsBySeries(activeSeries);
|
|
||||||
const startTime = DateTime.fromMillis(activeWidget.startTimestamp).toFormat('LLL dd, yyyy HH:mm a');
|
|
||||||
const endTime = DateTime.fromMillis(activeWidget.endTimestamp).toFormat('LLL dd, yyyy HH:mm a');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SlideModal
|
|
||||||
title={ activeWidget && (
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="mr-auto">{ activeWidget.widget.name } </div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
isDisplayed={ !!activeWidget }
|
|
||||||
onClose={ () => props.setActiveWidget(null)}
|
|
||||||
content={ activeWidget && (
|
|
||||||
<div className={ stl.wrapper }>
|
|
||||||
<div className="mb-6 flex items-center">
|
|
||||||
<div className="mr-auto">Showing all sessions between <span className="font-medium">{startTime}</span> and <span className="font-medium">{endTime}</span> </div>
|
|
||||||
<div className="flex items-center ml-6">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<span className="mr-2 color-gray-medium">Timezone</span>
|
|
||||||
<TimezoneDropdown />
|
|
||||||
</div>
|
|
||||||
{ activeWidget.widget.metricType !== 'table' && (
|
|
||||||
<div className="flex items-center ml-6">
|
|
||||||
<span className="mr-2 color-gray-medium">Series</span>
|
|
||||||
<Dropdown
|
|
||||||
className={stl.dropdown}
|
|
||||||
direction="left"
|
|
||||||
options={ seriesOptions }
|
|
||||||
name="change"
|
|
||||||
value={ activeSeries }
|
|
||||||
onChange={ writeOption }
|
|
||||||
id="change-dropdown"
|
|
||||||
// icon={null}
|
|
||||||
icon={ <Icon name="chevron-down" color="gray-dark" size="14" className={stl.dropdownIcon} /> }
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* <span className="mr-2 color-gray-medium">Series</span> */}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Loader loading={loading}>
|
|
||||||
<NoContent
|
|
||||||
show={ !loading && (filteredSessions.length === 0 )}
|
|
||||||
title={
|
|
||||||
<div className="flex flex-col items-center justify-center">
|
|
||||||
<AnimatedSVG name={ICONS.NO_RESULTS} size={60} />
|
|
||||||
<div className="mt-6 text-2xl">No recordings found!</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
// animatedIcon="no-results"
|
|
||||||
>
|
|
||||||
{ filteredSessions.map(session => <SessionItem key={ session.sessionId } session={ session } />) }
|
|
||||||
</NoContent>
|
|
||||||
</Loader>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(state => ({
|
|
||||||
loading: state.getIn(['customMetrics', 'fetchSessionList', 'loading']),
|
|
||||||
list: state.getIn(['customMetrics', 'sessionList']),
|
|
||||||
// activeWidget: state.getIn(['customMetrics', 'activeWidget']),
|
|
||||||
}), { fetchSessionList, setActiveWidget })(SessionListModal);
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export { default } from './SessionListModal';
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export { default } from './CustomMetrics';
|
|
||||||
|
|
@ -1,203 +0,0 @@
|
||||||
import { List, Map } from 'immutable';
|
|
||||||
import CustomMetric, { FilterSeries } from 'Types/customMetric'
|
|
||||||
import { fetchListType, fetchType, saveType, removeType, editType, createRemove, createEdit } from './funcTools/crud';
|
|
||||||
import { createRequestReducer, ROOT_KEY } from './funcTools/request';
|
|
||||||
import { array, success, createListUpdater, mergeReducers } from './funcTools/tools';
|
|
||||||
import Filter from 'Types/filter';
|
|
||||||
import Session from 'Types/session';
|
|
||||||
|
|
||||||
const name = "custom_metric";
|
|
||||||
const idKey = "metricId";
|
|
||||||
|
|
||||||
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`;
|
|
||||||
const SET_ACTIVE_WIDGET = `${name}/SET_ACTIVE_WIDGET`;
|
|
||||||
const REMOVE = removeType(name);
|
|
||||||
const UPDATE_SERIES = `${name}/UPDATE_SERIES`;
|
|
||||||
|
|
||||||
const updateItemInList = createListUpdater(idKey);
|
|
||||||
const updateInstance = (state, instance) => state.getIn([ "instance", idKey ]) === instance[ idKey ]
|
|
||||||
? state.mergeIn([ "instance" ], instance)
|
|
||||||
: state;
|
|
||||||
|
|
||||||
const defaultInstance = CustomMetric({
|
|
||||||
name: 'New',
|
|
||||||
series: List([
|
|
||||||
{
|
|
||||||
name: 'Series 1',
|
|
||||||
filter: new Filter({ filters: List(), eventsOrder: 'then' }),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
const initialState = Map({
|
|
||||||
list: List(),
|
|
||||||
sessionList: List(),
|
|
||||||
alertMetricId: null,
|
|
||||||
instance: null,
|
|
||||||
activeWidget: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Metric - Series - [] - filters
|
|
||||||
function reducer(state = initialState, action = {}) {
|
|
||||||
switch (action.type) {
|
|
||||||
// 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:
|
|
||||||
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);
|
|
||||||
case success(REMOVE):
|
|
||||||
return state.update('list', list => list.filter(item => item.metricId !== action.id));
|
|
||||||
case success(FETCH):
|
|
||||||
return state.set("instance", CustomMetric(action.data));
|
|
||||||
case success(FETCH_LIST):
|
|
||||||
const { data } = action;
|
|
||||||
return state.set("list", List(data.map(CustomMetric)));
|
|
||||||
case success(FETCH_SESSION_LIST):
|
|
||||||
return state.set("sessionList", List(action.data.map(item => ({ ...item, sessions: item.sessions.map(s => new Session(s)) }))));
|
|
||||||
case SET_ACTIVE_WIDGET:
|
|
||||||
return state.set("activeWidget", action.widget).set('sessionList', List());
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default mergeReducers(
|
|
||||||
reducer,
|
|
||||||
createRequestReducer({
|
|
||||||
[ ROOT_KEY ]: FETCH_LIST,
|
|
||||||
fetch: FETCH,
|
|
||||||
save: SAVE,
|
|
||||||
fetchSessionList: FETCH_SESSION_LIST,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
export const edit = createEdit(name);
|
|
||||||
export const remove = createRemove(name);
|
|
||||||
|
|
||||||
export function fetch(id) {
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
types: array(FETCH),
|
|
||||||
call: c => c.get(`/errors/${id}`),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const save = (instance) => (dispatch, getState) => {
|
|
||||||
return dispatch({
|
|
||||||
types: SAVE.array,
|
|
||||||
call: client => client.post( `/${ instance.exists() ? name + 's/' + instance[idKey] : name + 's'}`, instance.toSaveData()),
|
|
||||||
}).then(() => {
|
|
||||||
dispatch(fetchList());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export function fetchList() {
|
|
||||||
return {
|
|
||||||
types: array(FETCH_LIST),
|
|
||||||
call: client => client.get(`/${name}s`),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
call: client => client.post(`/custom_metrics/${params.metricId}/sessions`, { ...params }),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const setActiveWidget = (widget) => (dispatch, getState) => {
|
|
||||||
return dispatch({
|
|
||||||
type: SET_ACTIVE_WIDGET,
|
|
||||||
widget,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const updateActiveState = (metricId, state) => (dispatch, getState) => {
|
|
||||||
return dispatch({
|
|
||||||
types: UPDATE_ACTIVE_STATE.array,
|
|
||||||
call: client => client.post(`/custom_metrics/${metricId}/status`, { active: state }),
|
|
||||||
metricId
|
|
||||||
}).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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
@ -1,375 +0,0 @@
|
||||||
import { List, Map, Set } from 'immutable';
|
|
||||||
import { errors as errorsRoute } from "App/routes";
|
|
||||||
import Filter from 'Types/filter';
|
|
||||||
import SavedFilter from 'Types/filter/savedFilter';
|
|
||||||
import Event from 'Types/filter/event';
|
|
||||||
import CustomFilter from 'Types/filter/customFilter';
|
|
||||||
import withRequestState, { RequestTypes } from './requestStateCreator';
|
|
||||||
import { fetch as fetchFunnel, fetchInsights, fetchIssuesFiltered, fetchSessionsFiltered } from './funnels';
|
|
||||||
|
|
||||||
const ERRORS_ROUTE = errorsRoute();
|
|
||||||
|
|
||||||
const FETCH_LIST = new RequestTypes('funnelFilters/FETCH_LIST');
|
|
||||||
const FETCH_FILTER_OPTIONS = new RequestTypes('funnelFilters/FETCH_FILTER_OPTIONS');
|
|
||||||
const SET_FILTER_OPTIONS = 'funnelFilters/SET_FILTER_OPTIONS';
|
|
||||||
const SAVE = new RequestTypes('funnelFilters/SAVE');
|
|
||||||
const REMOVE = new RequestTypes('funnelFilters/REMOVE');
|
|
||||||
|
|
||||||
const RESET = 'funnelFilters/RESET';
|
|
||||||
const SET_SEARCH_QUERY = 'funnelFilters/SET_SEARCH_QUERY';
|
|
||||||
const SET_ACTIVE = 'funnelFilters/SET_ACTIVE';
|
|
||||||
const SET_ACTIVE_KEY = 'funnelFilters/SET_ACTIVE_KEY';
|
|
||||||
const APPLY = 'funnelFilters/APPLY';
|
|
||||||
const ADD_CUSTOM_FILTER = 'funnelFilters/ADD_CUSTOM_FILTER';
|
|
||||||
const REMOVE_CUSTOM_FILTER = 'funnelFilters/REMOVE_CUSTOM_FILTER';
|
|
||||||
const RESET_KEY = 'funnelFilters/RESET_KEY';
|
|
||||||
const ADD_EVENT = 'funnelFilters/ADD_EVENT';
|
|
||||||
const EDIT_EVENT = 'funnelFilters/EDIT_EVENT';
|
|
||||||
const REMOVE_EVENT = 'funnelFilters/REMOVE_EVENT';
|
|
||||||
const MOVE_EVENT = 'funnelFilters/MOVE_EVENT';
|
|
||||||
const CLEAR_EVENTS = 'funnelFilters/CLEAR_EVENTS';
|
|
||||||
const TOGGLE_FILTER_MODAL = 'funnelFilters/TOGGLE_FILTER_MODAL';
|
|
||||||
const ADD_ATTRIBUTE = 'funnelFilters/ADD_ATTRIBUTE';
|
|
||||||
const EDIT_ATTRIBUTE = 'funnelFilters/EDIT_ATTRIBUTE';
|
|
||||||
const REMOVE_ATTRIBUTE = 'funnelFilters/REMOVE_ATTRIBUTE';
|
|
||||||
const SET_ACTIVE_FLOW = 'funnelFilters/SET_ACTIVE_FLOW';
|
|
||||||
|
|
||||||
const SET_INITIAL_FILTER = 'funnelFilters/SET_INITIAL_FILTER';
|
|
||||||
|
|
||||||
const initialState = Map({
|
|
||||||
activeFilter: null,
|
|
||||||
list: List(),
|
|
||||||
appliedFilter: Filter(),
|
|
||||||
activeFilterKey: null,
|
|
||||||
saveModalOpen: false,
|
|
||||||
customFilters: Map(),
|
|
||||||
searchQuery: '',
|
|
||||||
activeFlow: null,
|
|
||||||
filterOptions: Map({
|
|
||||||
USEROS: Set(),
|
|
||||||
USERBROWSER: Set(),
|
|
||||||
USERDEVICE: Set(),
|
|
||||||
REFERRER: Set(),
|
|
||||||
USERCOUNTRY: Set(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
let hasFilterOptions = false;
|
|
||||||
|
|
||||||
const updateList = (state, instance) => state.update('list', (list) => {
|
|
||||||
const index = list.findIndex(item => item.filterId === instance.filterId);
|
|
||||||
return (index >= 0
|
|
||||||
? list.mergeIn([ index ], instance)
|
|
||||||
: list.push(instance)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const reducer = (state = initialState, action = {}) => {
|
|
||||||
let optionsMap = null;
|
|
||||||
switch (action.type) {
|
|
||||||
case FETCH_FILTER_OPTIONS.SUCCESS:
|
|
||||||
// return state.mergeIn(['filterOptions', action.key], fromJS(action.data).map(item => ({text: item, value: item})));
|
|
||||||
optionsMap = state.getIn(['filterOptions', action.key]).map(i => i.value).toJS();
|
|
||||||
return state.mergeIn(['filterOptions', action.key], Set(action.data.filter(i => !optionsMap.includes(i.value))));
|
|
||||||
case SET_FILTER_OPTIONS:
|
|
||||||
// optionsMap = state.getIn(['filterOptions', action.key]);
|
|
||||||
// optionsMap = optionsMap ? optionsMap.map(i => i.value).toJS() : []
|
|
||||||
// return state.mergeIn(['filterOptions', action.key], Set(action.filterOption.filter(i => !optionsMap.includes(i.value))));
|
|
||||||
const tmp = {}
|
|
||||||
let _state = state;
|
|
||||||
action.filters.forEach(f => {
|
|
||||||
if (f.type && f.value && f.value.length > 0) {
|
|
||||||
tmp[f.type] = tmp[f.type] ? tmp[f.type].concat(f.value) : f.value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
Object.keys(tmp).forEach(f => {
|
|
||||||
const options = List(tmp[f]).map(i => ({type: i, value: i})) // TODO should get the unique items
|
|
||||||
_state = _state.mergeIn(['filterOptions', f], options);
|
|
||||||
})
|
|
||||||
|
|
||||||
return _state;
|
|
||||||
case FETCH_LIST.SUCCESS:
|
|
||||||
return state;
|
|
||||||
case SAVE.SUCCESS:
|
|
||||||
return updateList(state, SavedFilter(action.data))
|
|
||||||
.set('saveModalOpen', false);
|
|
||||||
case REMOVE.SUCCESS:
|
|
||||||
return state.update(
|
|
||||||
'list',
|
|
||||||
list => list
|
|
||||||
.filter(filter => filter.filterId !== action.id),
|
|
||||||
).set('activeFilter', null);
|
|
||||||
case SET_ACTIVE:
|
|
||||||
return state.set('activeFilter', action.filter);
|
|
||||||
case SET_ACTIVE_FLOW:
|
|
||||||
return state.set('activeFlow', action.flow);
|
|
||||||
case SET_ACTIVE_KEY:
|
|
||||||
return state.set('activeFilterKey', action.filterKey);
|
|
||||||
case APPLY:
|
|
||||||
return action.fromUrl
|
|
||||||
? state.set('appliedFilter',
|
|
||||||
Filter(action.filter)
|
|
||||||
// .set('events', state.getIn([ 'appliedFilter', 'events' ]))
|
|
||||||
)
|
|
||||||
: state.mergeIn(['instance', 'filter'], action.filter);
|
|
||||||
case ADD_CUSTOM_FILTER:
|
|
||||||
return state.update('customFilters', vars => vars.set(action.filter, action.value));
|
|
||||||
case REMOVE_CUSTOM_FILTER:
|
|
||||||
return state.update('customFilters', vars => vars.remove(action.filterKey));
|
|
||||||
case RESET_KEY:
|
|
||||||
if (action.key === 'rangeValue') {
|
|
||||||
return state
|
|
||||||
.removeIn([ 'appliedFilter', 'rangeValue' ])
|
|
||||||
.removeIn([ 'appliedFilter', 'startDate' ])
|
|
||||||
.removeIn([ 'appliedFilter', 'endDate' ]);
|
|
||||||
} else if (action.key === 'duration') {
|
|
||||||
return state
|
|
||||||
.removeIn([ 'appliedFilter', 'minDuration' ])
|
|
||||||
.removeIn([ 'appliedFilter', 'maxDuration' ]);
|
|
||||||
}
|
|
||||||
return state.removeIn([ 'appliedFilter', action.key ]);
|
|
||||||
case ADD_EVENT:
|
|
||||||
const eventValue = action.event.value;
|
|
||||||
const event = Event(action.event).set('value', eventValue);
|
|
||||||
if (action.index >= 0) // replacing an event
|
|
||||||
return state.setIn([ 'appliedFilter', 'events', action.index ], event)
|
|
||||||
else
|
|
||||||
return state.updateIn([ 'appliedFilter', 'events' ], list => action.single
|
|
||||||
? List([ event ])
|
|
||||||
: list.push(event));
|
|
||||||
case REMOVE_EVENT:
|
|
||||||
return state.removeIn([ 'appliedFilter', 'events', action.index ]);
|
|
||||||
case EDIT_EVENT:
|
|
||||||
return state.mergeIn([ 'appliedFilter', 'events', action.index], action.filter);
|
|
||||||
case TOGGLE_FILTER_MODAL:
|
|
||||||
return state.set('saveModalOpen', action.show);
|
|
||||||
case MOVE_EVENT:
|
|
||||||
const { fromI, toI } = action;
|
|
||||||
return state
|
|
||||||
.updateIn([ 'appliedFilter', 'events' ], list =>
|
|
||||||
list.remove(fromI).insert(toI, list.get(fromI)));
|
|
||||||
case CLEAR_EVENTS:
|
|
||||||
return state.setIn([ 'appliedFilter', 'events' ], List())
|
|
||||||
.setIn([ 'appliedFilter', 'filters' ], List())
|
|
||||||
.set('searchQuery', '');
|
|
||||||
|
|
||||||
case ADD_ATTRIBUTE:
|
|
||||||
const filter = CustomFilter(action.filter);
|
|
||||||
|
|
||||||
if (action.index >= 0) // replacing the filter
|
|
||||||
return state.setIn([ 'appliedFilter', 'filters', action.index], filter);
|
|
||||||
else
|
|
||||||
return state.updateIn([ 'appliedFilter', 'filters'], filters => filters.push(filter));
|
|
||||||
case EDIT_ATTRIBUTE:
|
|
||||||
return state.setIn([ 'appliedFilter', 'filters', action.index, action.key ], action.value );
|
|
||||||
case REMOVE_ATTRIBUTE:
|
|
||||||
return state.removeIn([ 'appliedFilter', 'filters', action.index ]);
|
|
||||||
case SET_SEARCH_QUERY:
|
|
||||||
return state.set('searchQuery', action.query);
|
|
||||||
case RESET:
|
|
||||||
return state.set('appliedFilter', Filter({}))
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withRequestState({
|
|
||||||
_: [ REMOVE ],
|
|
||||||
fetchListRequest: FETCH_LIST,
|
|
||||||
saveRequest: SAVE,
|
|
||||||
fetchFilterOptions: FETCH_FILTER_OPTIONS,
|
|
||||||
}, reducer);
|
|
||||||
|
|
||||||
const eventMap = ({value, type, key, operator, source, custom}) => ({value, type, key, operator, source, custom});
|
|
||||||
const filterMap = ({value, type, key, operator, source, custom }) => ({value: Array.isArray(value) ? value: [value], custom, type, key, operator, source});
|
|
||||||
const reduceThenFetchResource = actionCreator => (...args) => (dispatch, getState) => {
|
|
||||||
const action = actionCreator(...args);
|
|
||||||
dispatch(action);
|
|
||||||
const appliedFilters = getState().getIn([ 'funnelFilters', 'appliedFilter' ]);
|
|
||||||
const filter = appliedFilters
|
|
||||||
.update('events', list => list.map(event => event.set('value', event.value || '*')).map(eventMap))
|
|
||||||
.toJS();
|
|
||||||
|
|
||||||
filter.filters = getState().getIn([ 'funnelFilters', 'appliedFilter', 'filters' ])
|
|
||||||
.map(filterMap).toJS();
|
|
||||||
|
|
||||||
if (action.funnelId) {
|
|
||||||
dispatch(fetchFunnel(action.funnelId))
|
|
||||||
dispatch(fetchInsights(action.funnelId, filter))
|
|
||||||
dispatch(fetchIssuesFiltered(action.funnelId, filter))
|
|
||||||
dispatch(fetchSessionsFiltered(action.funnelId, filter))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function editAttribute(index, key, value) {
|
|
||||||
return {
|
|
||||||
type: EDIT_ATTRIBUTE,
|
|
||||||
index,
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addAttribute(filter, index) {
|
|
||||||
return {
|
|
||||||
type: ADD_ATTRIBUTE,
|
|
||||||
filter,
|
|
||||||
index
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeAttribute(index) {
|
|
||||||
return {
|
|
||||||
type: REMOVE_ATTRIBUTE,
|
|
||||||
index,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchList(range) {
|
|
||||||
return {
|
|
||||||
types: FETCH_LIST.toArray(),
|
|
||||||
call: client => client.get(`/funnels`),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchFilterOptions(filter, q) {
|
|
||||||
return {
|
|
||||||
types: FETCH_FILTER_OPTIONS.toArray(),
|
|
||||||
call: client => client.get('/sessions/filters/search', { q, type: filter.type }),
|
|
||||||
key: filter.key
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setFilterOptions(filters) {
|
|
||||||
return {
|
|
||||||
type: SET_FILTER_OPTIONS,
|
|
||||||
filters
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function save(instance) {
|
|
||||||
return {
|
|
||||||
types: SAVE.toArray(),
|
|
||||||
call: client => client.post('/filters', instance.toData()),
|
|
||||||
instance,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function remove(id) {
|
|
||||||
return {
|
|
||||||
types: REMOVE.toArray(),
|
|
||||||
call: client => client.delete(`/filters/${ id }`),
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setActive(filter) {
|
|
||||||
return {
|
|
||||||
type: SET_ACTIVE,
|
|
||||||
filter,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setActiveFlow(flow) {
|
|
||||||
return {
|
|
||||||
type: SET_ACTIVE_FLOW,
|
|
||||||
flow,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setActiveKey(filterKey) {
|
|
||||||
return {
|
|
||||||
type: SET_ACTIVE_KEY,
|
|
||||||
filterKey,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const addCustomFilter = reduceThenFetchResource((filter, value) => ({
|
|
||||||
type: ADD_CUSTOM_FILTER,
|
|
||||||
filter,
|
|
||||||
value,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const removeCustomFilter = reduceThenFetchResource(filterKey => ({
|
|
||||||
type: REMOVE_CUSTOM_FILTER,
|
|
||||||
filterKey,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const applyFilter = reduceThenFetchResource((filter, funnelId, fromUrl=false) => ({
|
|
||||||
type: APPLY,
|
|
||||||
filter,
|
|
||||||
funnelId,
|
|
||||||
fromUrl,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const setInitialFilters = () => (dispatch, getState) => {
|
|
||||||
return dispatch({
|
|
||||||
type: APPLY,
|
|
||||||
filter: getState().getIn(['funnels', 'instance', 'filter'])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const applySavedFilter = reduceThenFetchResource((filter, fromUrl=false) => ({
|
|
||||||
type: APPLY,
|
|
||||||
filter,
|
|
||||||
fromUrl,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const resetFilterKey = reduceThenFetchResource(key => ({
|
|
||||||
type: RESET_KEY,
|
|
||||||
key,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const clearEvents = reduceThenFetchResource(() => ({
|
|
||||||
type: CLEAR_EVENTS,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export function addEvent(event, single = false, index) {
|
|
||||||
return {
|
|
||||||
type: ADD_EVENT,
|
|
||||||
event,
|
|
||||||
single,
|
|
||||||
index
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const removeEvent = reduceThenFetchResource((index, funnelId) => ({
|
|
||||||
type: REMOVE_EVENT,
|
|
||||||
index,
|
|
||||||
funnelId
|
|
||||||
}));
|
|
||||||
|
|
||||||
export function moveEvent(fromI, toI) {
|
|
||||||
return {
|
|
||||||
type: MOVE_EVENT,
|
|
||||||
fromI,
|
|
||||||
toI,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const editEvent = reduceThenFetchResource((index, filter, funnelId) => ({
|
|
||||||
type: EDIT_EVENT,
|
|
||||||
index,
|
|
||||||
filter,
|
|
||||||
funnelId
|
|
||||||
}))
|
|
||||||
|
|
||||||
export function toggleFilterModal(show) {
|
|
||||||
return {
|
|
||||||
type: TOGGLE_FILTER_MODAL,
|
|
||||||
show,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setSearchQuery(query) {
|
|
||||||
return {
|
|
||||||
type: SET_SEARCH_QUERY,
|
|
||||||
query
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resetFunnelFilters() {
|
|
||||||
return {
|
|
||||||
type: RESET
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,7 +9,6 @@ import site from './site';
|
||||||
import customFields from './customField';
|
import customFields from './customField';
|
||||||
import integrations from './integrations';
|
import integrations from './integrations';
|
||||||
import errors from './errors';
|
import errors from './errors';
|
||||||
import customMetrics from './customMetrics';
|
|
||||||
import search from './search';
|
import search from './search';
|
||||||
import liveSearch from './liveSearch';
|
import liveSearch from './liveSearch';
|
||||||
|
|
||||||
|
|
@ -20,13 +19,12 @@ const rootReducer = combineReducers({
|
||||||
site,
|
site,
|
||||||
customFields,
|
customFields,
|
||||||
errors,
|
errors,
|
||||||
customMetrics,
|
|
||||||
search,
|
search,
|
||||||
liveSearch,
|
liveSearch,
|
||||||
...integrations,
|
...integrations,
|
||||||
...sources,
|
...sources
|
||||||
});
|
});
|
||||||
|
|
||||||
export type RootStore = ReturnType<typeof rootReducer>
|
export type RootStore = ReturnType<typeof rootReducer>
|
||||||
|
|
||||||
export default rootReducer
|
export default rootReducer;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue