feat(ui) - pagination wip

This commit is contained in:
Shekar Siri 2022-03-10 17:26:07 +01:00
parent 667808b275
commit 09aca1e61d
12 changed files with 123 additions and 83 deletions

View file

@ -13,7 +13,8 @@ import withLocationHandlers from "HOCs/withLocationHandlers";
import { fetch as fetchFilterVariables } from 'Duck/sources';
import { fetchSources } from 'Duck/customField';
import { RehydrateSlidePanel } from './WatchDogs/components';
import { setActiveTab, setFunnelPage } from 'Duck/sessions';
import { setFunnelPage } from 'Duck/sessions';
import { setActiveTab } from 'Duck/search';
import SessionsMenu from './SessionsMenu/SessionsMenu';
import { LAST_7_DAYS } from 'Types/app/period';
import { resetFunnel } from 'Duck/funnels';
@ -51,12 +52,12 @@ const allowedQueryKeys = [
variables: state.getIn([ 'customFields', 'list' ]),
sources: state.getIn([ 'customFields', 'sources' ]),
filterValues: state.get('filterValues'),
activeTab: state.getIn([ 'sessions', 'activeTab' ]),
favoriteList: state.getIn([ 'sessions', 'favoriteList' ]),
currentProjectId: state.getIn([ 'user', 'siteId' ]),
sites: state.getIn([ 'site', 'list' ]),
watchdogs: state.getIn(['watchdogs', 'list']),
activeFlow: state.getIn([ 'filters', 'activeFlow' ]),
sessions: state.getIn([ 'sessions', 'list' ]),
}), {
fetchFavoriteSessionList,
applyFilter,
@ -91,7 +92,9 @@ export default class BugFinder extends React.PureComponent {
// keys: this.props.sources.filter(({type}) => type === 'logTool').map(({ label, key }) => ({ type: 'ERROR', source: key, label: label, key, icon: 'integrations/' + key, isFilter: false })).toJS()
// };
// });
props.fetchSessions();
if (props.sessions.size === 0) {
props.fetchSessions();
}
props.resetFunnel();
props.resetFunnelFilters();
props.fetchFunnelsList(LAST_7_DAYS)
@ -115,7 +118,6 @@ export default class BugFinder extends React.PureComponent {
}
render() {
const { activeFlow, activeTab } = this.props;
const { showRehydratePanel } = this.state;
return (

View file

@ -5,7 +5,6 @@ import { fetchSessions, addFilterByKeyAndValue, updateCurrentPage } from 'Duck/s
import SessionItem from 'Shared/SessionItem';
import SessionListHeader from './SessionListHeader';
import { FilterKey } from 'Types/filter/filterType';
import { sliceListPerPage } from 'App/utils';
const ALL = 'all';
const PER_PAGE = 5;
@ -16,7 +15,7 @@ var timeoutId;
shouldAutorefresh: state.getIn([ 'filters', 'appliedFilter', 'events' ]).size === 0,
savedFilters: state.getIn([ 'filters', 'list' ]),
loading: state.getIn([ 'sessions', 'loading' ]),
activeTab: state.getIn([ 'sessions', 'activeTab' ]),
activeTab: state.getIn([ 'search', 'activeTab' ]),
allList: state.getIn([ 'sessions', 'list' ]),
total: state.getIn([ 'sessions', 'total' ]),
filters: state.getIn([ 'search', 'instance', 'filters' ]),
@ -90,6 +89,7 @@ export default class SessionList extends React.PureComponent {
activeTab,
metaList,
currentPage,
total,
} = this.props;
const _filterKeys = filters.map(i => i.key);
const hasUserFilter = _filterKeys.includes(FilterKey.USERID) || _filterKeys.includes(FilterKey.USERANONYMOUSID);
@ -120,7 +120,7 @@ export default class SessionList extends React.PureComponent {
}
>
<Loader loading={ loading }>
{ sliceListPerPage(list, currentPage, PER_PAGE).map(session => (
{ list.map(session => (
<SessionItem
key={ session.sessionId }
session={ session }
@ -130,11 +130,13 @@ export default class SessionList extends React.PureComponent {
/>
))}
</Loader>
<Pagination
page={currentPage}
totalPages={Math.ceil(list.size / PER_PAGE)}
onPageChange={(page) => this.props.updateCurrentPage(page)}
/>
<div className="w-full flex items-center justify-center py-6">
<Pagination
page={currentPage}
totalPages={Math.ceil(total / PER_PAGE)}
onPageChange={(page) => this.props.updateCurrentPage(page)}
/>
</div>
{/* <LoadMoreButton
className="mt-12 mb-12"
displayedCount={displayedCount}

View file

@ -64,5 +64,5 @@ function SessionListHeader({
};
export default connect(state => ({
activeTab: state.getIn([ 'sessions', 'activeTab' ]),
activeTab: state.getIn([ 'search', 'activeTab' ]),
}), { applyFilter })(SessionListHeader);

View file

@ -1,30 +1,20 @@
import React, { useEffect } from 'react'
import React from 'react'
import { connect } from 'react-redux';
import cn from 'classnames';
import { SideMenuitem, SavedSearchList, Progress, Popup, Icon, CircularLoader } from 'UI'
import { SideMenuitem, SavedSearchList, Progress, Popup } from 'UI'
import stl from './sessionMenu.css';
import { fetchWatchdogStatus } from 'Duck/watchdogs';
import { setActiveFlow, clearEvents } from 'Duck/filters';
import { setActiveTab } from 'Duck/sessions';
import { clearEvents } from 'Duck/filters';
import { issues_types } from 'Types/session/issue'
import { fetchList as fetchSessionList } from 'Duck/sessions';
function SessionsMenu(props) {
const {
activeFlow, activeTab, watchdogs = [], keyMap, wdTypeCount,
fetchWatchdogStatus, toggleRehydratePanel, filters, sessionsLoading } = props;
const { activeTab, keyMap, wdTypeCount, toggleRehydratePanel } = props;
const onMenuItemClick = (filter) => {
props.onMenuItemClick(filter)
if (activeFlow && activeFlow.type === 'flows') {
props.setActiveFlow(null)
}
}
// useEffect(() => {
// fetchWatchdogStatus()
// }, [])
const capturingAll = props.captureRate && props.captureRate.get('captureAll');
@ -66,36 +56,13 @@ function SessionsMenu(props) {
{ issues_types.filter(item => item.visible).map(item => (
<SideMenuitem
key={item.key}
disabled={!keyMap[item.type] && !wdTypeCount[item.type]}
// disabled={!keyMap[item.type] && !wdTypeCount[item.type]}
active={activeTab.type === item.type}
title={item.name} iconName={item.icon}
onClick={() => onMenuItemClick(item)}
/>
))}
{/* <div className={stl.divider} />
<div className="my-3">
<SideMenuitem
title={
<div className="flex items-center">
<div>Assist</div>
{ activeTab.type === 'live' && (
<div
className="ml-4 h-5 w-6 flex items-center justify-center"
onClick={() => !sessionsLoading && props.fetchSessionList(filters.toJS())}
>
{ sessionsLoading ? <CircularLoader className="ml-1" /> : <Icon name="sync-alt" size="14" />}
</div>
)}
</div>
}
iconName="person"
active={activeTab.type === 'live'}
onClick={() => onMenuItemClick({ name: 'Assist', type: 'live' })}
/>
</div> */}
<div className={stl.divider} />
<div className="my-3">
<SideMenuitem
@ -113,13 +80,12 @@ function SessionsMenu(props) {
}
export default connect(state => ({
activeTab: state.getIn([ 'sessions', 'activeTab' ]),
activeTab: state.getIn([ 'search', 'activeTab' ]),
keyMap: state.getIn([ 'sessions', 'keyMap' ]),
wdTypeCount: state.getIn([ 'sessions', 'wdTypeCount' ]),
activeFlow: state.getIn([ 'filters', 'activeFlow' ]),
captureRate: state.getIn(['watchdogs', 'captureRate']),
filters: state.getIn([ 'filters', 'appliedFilter' ]),
sessionsLoading: state.getIn([ 'sessions', 'fetchLiveListRequest', 'loading' ]),
}), {
fetchWatchdogStatus, setActiveFlow, clearEvents, setActiveTab, fetchSessionList
fetchWatchdogStatus, clearEvents, fetchSessionList
})(SessionsMenu);

View file

@ -10,8 +10,12 @@ interface Props {
}
export default function Pagination(props: Props) {
const { page, totalPages, onPageChange, limit = 5 } = props;
const [currentPage, setCurrentPage] = React.useState(page);
// const []
React.useMemo(
() => setCurrentPage(page),
[page],
);
const changePage = (page: number) => {
if (page > 0 && page <= totalPages) {
@ -19,6 +23,7 @@ export default function Pagination(props: Props) {
setCurrentPage(page);
}
}
return (
<div className="flex items-center">
<button
@ -31,7 +36,7 @@ export default function Pagination(props: Props) {
<span className="mr-2 color-gray-medium">Page</span>
<input
type="number"
className={cn("py-1 px-2 bg-white border rounded w-16", { "opacity-50 cursor-default": totalPages === 1 })}
className={cn("py-1 px-2 bg-white border border-gray-light rounded w-16", { "opacity-50 cursor-default": totalPages === 1 })}
value={currentPage}
min={1}
max={totalPages}

View file

@ -1,4 +1,4 @@
import { FilterKey } from 'Types/filter/filterType';
import { FilterKey, IssueType } from 'Types/filter/filterType';
export const options = [
{ key: 'on', text: 'on', value: 'on' },
@ -93,18 +93,18 @@ export const methodOptions = [
]
export const issueOptions = [
{ text: 'Click Rage', value: 'click_rage' },
{ text: 'Dead Click', value: 'dead_click' },
{ text: 'Excessive Scrolling', value: 'excessive_scrolling' },
{ text: 'Bad Request', value: 'bad_request' },
{ text: 'Missing Resource', value: 'missing_resource' },
{ text: 'Memory', value: 'memory' },
{ text: 'CPU', value: 'cpu' },
{ text: 'Slow Resource', value: 'slow_resource' },
{ text: 'Slow Page Load', value: 'slow_page_load' },
{ text: 'Crash', value: 'crash' },
{ text: 'Custom', value: 'custom' },
{ text: 'JS Exception', value: 'js_exception' },
{ text: 'Click Rage', value: IssueType.CLICK_RAGE },
{ text: 'Dead Click', value: IssueType.DEAD_CLICK },
{ text: 'Excessive Scrolling', value: IssueType.EXCESSIVE_SCROLLING },
{ text: 'Bad Request', value: IssueType.BAD_REQUEST },
{ text: 'Missing Resource', value: IssueType.MISSING_RESOURCE },
{ text: 'Memory', value: IssueType.MEMORY },
{ text: 'CPU', value: IssueType.CPU },
{ text: 'Slow Resource', value: IssueType.SLOW_RESOURCE },
{ text: 'Slow Page Load', value: IssueType.SLOW_PAGE_LOAD },
{ text: 'Crash', value: IssueType.CRASH },
{ text: 'Custom', value: IssueType.CUSTOM },
{ text: 'Error', value: IssueType.JS_EXCEPTION },
]
export default {

View file

@ -7,7 +7,7 @@ import SavedFilter from 'Types/filter/savedFilter';
import { errors as errorsRoute, isRoute } from "App/routes";
import { fetchList as fetchSessionList } from './sessions';
import { fetchList as fetchErrorsList } from './errors';
import { FilterCategory, FilterKey } from 'Types/filter/filterType';
import { FilterCategory, FilterKey, IssueType } from 'Types/filter/filterType';
import { filtersMap, liveFiltersMap, generateFilterOptions, generateLiveFilterOptions } from 'Types/filter/newFilter';
const ERRORS_ROUTE = errorsRoute();
@ -29,6 +29,7 @@ const UPDATE = `${name}/UPDATE`;
const APPLY = `${name}/APPLY`;
const SET_ALERT_METRIC_ID = `${name}/SET_ALERT_METRIC_ID`;
const UPDATE_CURRENT_PAGE = `${name}/UPDATE_CURRENT_PAGE`;
const SET_ACTIVE_TAB = `${name}/SET_ACTIVE_TAB`;
const REFRESH_FILTER_OPTIONS = 'filters/REFRESH_FILTER_OPTIONS';
@ -51,6 +52,7 @@ const initialState = Map({
savedSearch: new SavedFilter({}),
filterSearchList: {},
currentPage: 1,
activeTab: {name: 'All', type: 'all' },
});
// Metric - Series - [] - filters
@ -64,7 +66,7 @@ function reducer(state = initialState, action = {}) {
case APPLY:
return action.fromUrl
? state.set('instance', Filter(action.filter))
: state.mergeIn(['instance'], action.filter);
: state.mergeIn(['instance'], action.filter).set('currentPage', 1);
case success(FETCH):
return state.set("instance", action.data);
case success(FETCH_LIST):
@ -87,6 +89,8 @@ function reducer(state = initialState, action = {}) {
return state.mergeIn([ 'savedSearch' ], action.instance);
case UPDATE_CURRENT_PAGE:
return state.set('currentPage', action.page);
case SET_ACTIVE_TAB:
return state.set('activeTab', action.tab).set('currentPage', 1);
}
return state;
}
@ -125,6 +129,18 @@ export const filterMap = ({category, value, key, operator, sourceOperator, sourc
const reduceThenFetchResource = actionCreator => (...args) => (dispatch, getState) => {
dispatch(actionCreator(...args));
const filter = getState().getIn([ 'search', 'instance']).toData();
const activeTab = getState().getIn([ 'search', 'activeTab']);
if (activeTab.type !== 'all' && activeTab.type !== 'bookmark') {
const tmpFilter = filtersMap[FilterKey.ISSUE];
tmpFilter.value = [activeTab.type]
filter.filters = filter.filters.concat(tmpFilter)
}
if (activeTab.type === 'bookmark') {
filter.bookmarked = true
}
filter.filters = filter.filters.map(filterMap);
filter.limit = 5;
filter.page = getState().getIn([ 'search', 'currentPage']);
@ -139,6 +155,11 @@ export const edit = reduceThenFetchResource((instance) => ({
instance,
}));
export const setActiveTab = reduceThenFetchResource((tab) => ({
type: SET_ACTIVE_TAB,
tab
}));
export const remove = (id) => (dispatch, getState) => {
return dispatch({
types: REMOVE.array,
@ -158,6 +179,11 @@ export const applyFilter = reduceThenFetchResource((filter, fromUrl=false) => ({
fromUrl,
}));
export const updateCurrentPage = reduceThenFetchResource((page) => ({
type: UPDATE_CURRENT_PAGE,
page,
}));
export const applySavedSearch = (filter) => (dispatch, getState) => {
dispatch(edit({ filters: filter ? filter.filter.filters : [] }));
return dispatch({
@ -274,11 +300,4 @@ export const refreshFilterOptions = () => {
return {
type: REFRESH_FILTER_OPTIONS
}
}
export function updateCurrentPage(page) {
return {
type: UPDATE_CURRENT_PAGE,
page,
};
}

View file

@ -60,7 +60,6 @@ const initialState = Map({
funnelPage: Map(),
timelinePointer: null,
sessionPath: '',
total: 0,
});
const reducer = (state = initialState, action = {}) => {
@ -130,7 +129,6 @@ const reducer = (state = initialState, action = {}) => {
.set('favoriteList', list.filter(({ favorite }) => favorite))
.set('total', total)
.set('keyMap', keyMap)
.set('total', total)
.set('wdTypeCount', wdTypeCount);
case SET_AUTOPLAY_VALUES: {
const sessionIds = state.get('sessionIds')

View file

@ -62,7 +62,7 @@
.color-white { color: $white }
.color-borderColor { color: $borderColor }
/* color */
/* hover color */
.hover-main:hover { color: $main }
.hover-gray-light-shade:hover { color: $gray-light-shade }
.hover-gray-lightest:hover { color: $gray-lightest }
@ -92,3 +92,33 @@
.hover-pink:hover { color: $pink }
.hover-white:hover { color: $white }
.hover-borderColor:hover { color: $borderColor }
.border-main { border-color: $main }
.border-gray-light-shade { border-color: $gray-light-shade }
.border-gray-lightest { border-color: $gray-lightest }
.border-gray-light { border-color: $gray-light }
.border-gray-medium { border-color: $gray-medium }
.border-gray-dark { border-color: $gray-dark }
.border-gray-darkest { border-color: $gray-darkest }
.border-teal { border-color: $teal }
.border-teal-dark { border-color: $teal-dark }
.border-teal-light { border-color: $teal-light }
.border-tealx { border-color: $tealx }
.border-tealx-light { border-color: $tealx-light }
.border-tealx-light-border { border-color: $tealx-light-border }
.border-orange { border-color: $orange }
.border-yellow { border-color: $yellow }
.border-yellow2 { border-color: $yellow2 }
.border-orange-dark { border-color: $orange-dark }
.border-green { border-color: $green }
.border-green2 { border-color: $green2 }
.border-green-dark { border-color: $green-dark }
.border-red { border-color: $red }
.border-red2 { border-color: $red2 }
.border-blue { border-color: $blue }
.border-blue2 { border-color: $blue2 }
.border-active-blue { border-color: $active-blue }
.border-active-blue-border { border-color: $active-blue-border }
.border-pink { border-color: $pink }
.border-white { border-color: $white }
.border-borderColor { border-color: $borderColor }

View file

@ -8,6 +8,21 @@ export enum FilterCategory {
PERFORMANCE = "Performance",
};
export enum IssueType {
CLICK_RAGE = "click_rage",
DEAD_CLICK = "dead_click",
EXCESSIVE_SCROLLING = "excessive_scrolling",
BAD_REQUEST = "bad_request",
MISSING_RESOURCE = "missing_resource",
MEMORY = "memory",
CPU = "cpu",
SLOW_RESOURCE = "slow_resource",
SLOW_PAGE_LOAD = "slow_page_load",
CRASH = "crash",
CUSTOM = "custom",
JS_EXCEPTION = "js_exception",
}
export enum FilterType {
STRING = "STRING",
ISSUE = "ISSUE",

View file

@ -237,5 +237,6 @@ export const isGreaterOrEqualVersion = (version, compareTo) => {
export const sliceListPerPage = (list, page, perPage = 10) => {
const start = page * perPage;
const end = start + perPage;
console.log(start, end)
return list.slice(start, end);
}

View file

@ -12,7 +12,9 @@ ${ colors.map(color => `.fill-${ color } { fill: $${ color } }`).join('\n') }
/* color */
${ colors.map(color => `.color-${ color } { color: $${ color } }`).join('\n') }
/* color */
/* hover color */
${ colors.map(color => `.hover-${ color }:hover { color: $${ color } }`).join('\n') }
${ colors.map(color => `.border-${ color } { border-color: $${ color } }`).join('\n') }
`)