From b0dda1de6628470a917cb653d8eb3eb829efef4b Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 8 Mar 2022 17:14:20 +0100 Subject: [PATCH 01/12] feature(ui) - pagination wip --- .../LiveSessionList/LiveSessionList.tsx | 16 +++++++-- .../components/ui/Pagination/Pagination.tsx | 35 +++++++++++++++++++ .../app/components/ui/Pagination/index.ts | 1 + frontend/app/components/ui/index.js | 1 + frontend/app/svg/icons/chevron-left.svg | 3 ++ frontend/app/svg/icons/chevron-right.svg | 3 ++ 6 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 frontend/app/components/ui/Pagination/Pagination.tsx create mode 100644 frontend/app/components/ui/Pagination/index.ts create mode 100644 frontend/app/svg/icons/chevron-left.svg create mode 100644 frontend/app/svg/icons/chevron-right.svg diff --git a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx index cb503a745..7d2490149 100644 --- a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx +++ b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react'; import { fetchLiveList } from 'Duck/sessions'; import { connect } from 'react-redux'; -import { NoContent, Loader, LoadMoreButton } from 'UI'; +import { NoContent, Loader, LoadMoreButton, Pagination } from 'UI'; import { List, Map } from 'immutable'; import SessionItem from 'Shared/SessionItem'; import withPermissions from 'HOCs/withPermissions' @@ -135,6 +135,13 @@ function LiveSessionList(props: Props) { props.updateSort({ order: state })} sortOrder={sort.order} /> +
+ null} + /> +
))} - */} + null} /> diff --git a/frontend/app/components/ui/Pagination/Pagination.tsx b/frontend/app/components/ui/Pagination/Pagination.tsx new file mode 100644 index 000000000..f61457bc5 --- /dev/null +++ b/frontend/app/components/ui/Pagination/Pagination.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import { Icon } from 'UI' +import cn from 'classnames' + +interface Props { + page: number + totalPages: number + onPageChange: (page: number) => void + limit?: number +} +export default function Pagination(props: Props) { + const { page, totalPages, onPageChange, limit = 5 } = props; + return ( +
+ + Page + + of + {totalPages} + +
+ ) +} diff --git a/frontend/app/components/ui/Pagination/index.ts b/frontend/app/components/ui/Pagination/index.ts new file mode 100644 index 000000000..29c341d81 --- /dev/null +++ b/frontend/app/components/ui/Pagination/index.ts @@ -0,0 +1 @@ +export { default } from './Pagination'; \ No newline at end of file diff --git a/frontend/app/components/ui/index.js b/frontend/app/components/ui/index.js index 1e0088720..1152437cf 100644 --- a/frontend/app/components/ui/index.js +++ b/frontend/app/components/ui/index.js @@ -55,5 +55,6 @@ export { default as HighlightCode } from './HighlightCode'; export { default as NoPermission } from './NoPermission'; export { default as NoSessionPermission } from './NoSessionPermission'; export { default as HelpText } from './HelpText'; +export { default as Pagination } from './Pagination'; export { Input, Modal, Form, Message, Card } from 'semantic-ui-react'; diff --git a/frontend/app/svg/icons/chevron-left.svg b/frontend/app/svg/icons/chevron-left.svg new file mode 100644 index 000000000..018f8b673 --- /dev/null +++ b/frontend/app/svg/icons/chevron-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/app/svg/icons/chevron-right.svg b/frontend/app/svg/icons/chevron-right.svg new file mode 100644 index 000000000..d621289b3 --- /dev/null +++ b/frontend/app/svg/icons/chevron-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file From 3537b6c00f7a084c350426cc2332db31ed4d2478 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 9 Mar 2022 15:58:20 +0100 Subject: [PATCH 02/12] feat(ui) - pagination wip --- .../LiveSessionList/LiveSessionList.tsx | 23 ++++++++------ .../components/ui/Pagination/Pagination.tsx | 30 ++++++++++++++----- .../app/svg/icons/chevron-double-left.svg | 2 +- frontend/app/svg/icons/chevron-left.svg | 2 +- frontend/app/svg/icons/chevron-right.svg | 2 +- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx index 7d2490149..c2f3c1b3b 100644 --- a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx +++ b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx @@ -42,9 +42,8 @@ function LiveSessionList(props: Props) { text: capitalize(i), value: i })).toJS(); - const displayedCount = Math.min(currentPage * PER_PAGE, sessions.size); - - const addPage = () => props.updateCurrentPage(props.currentPage + 1) + // const displayedCount = Math.min(currentPage * PER_PAGE, sessions.size); + // const addPage = () => props.updateCurrentPage(props.currentPage + 1) useEffect(() => { if (filters.size === 0) { @@ -108,6 +107,12 @@ function LiveSessionList(props: Props) { }, AUTOREFRESH_INTERVAL); } + const sliceListPerPage = (list, page) => { + const start = page * PER_PAGE; + const end = start + PER_PAGE; + return list.slice(start, end); + } + return (
@@ -138,8 +143,8 @@ function LiveSessionList(props: Props) {
null} + totalPages={Math.ceil(sessions.size / PER_PAGE)} + onPageChange={(page) => props.updateCurrentPage(page)} />
- {sessions && sessions.sortBy(i => i.metadata[sort.field]).update(list => { + {sessions && sliceListPerPage(sessions.sortBy(i => i.metadata[sort.field]).update(list => { return sort.order === 'desc' ? list.reverse() : list; - }).take(displayedCount).map(session => ( + }), currentPage - 1).map(session => ( */} - null} - /> + /> */}
diff --git a/frontend/app/components/ui/Pagination/Pagination.tsx b/frontend/app/components/ui/Pagination/Pagination.tsx index f61457bc5..11ae24c53 100644 --- a/frontend/app/components/ui/Pagination/Pagination.tsx +++ b/frontend/app/components/ui/Pagination/Pagination.tsx @@ -10,25 +10,41 @@ interface Props { } export default function Pagination(props: Props) { const { page, totalPages, onPageChange, limit = 5 } = props; + const [currentPage, setCurrentPage] = React.useState(page); + // const [] + + const changePage = (page: number) => { + if (page > 0 && page <= totalPages) { + onPageChange(page); + setCurrentPage(page); + } + } return (
Page - + changePage(parseInt(e.target.value))} + /> of {totalPages}
) diff --git a/frontend/app/svg/icons/chevron-double-left.svg b/frontend/app/svg/icons/chevron-double-left.svg index 7181fd111..8f30320c6 100644 --- a/frontend/app/svg/icons/chevron-double-left.svg +++ b/frontend/app/svg/icons/chevron-double-left.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/frontend/app/svg/icons/chevron-left.svg b/frontend/app/svg/icons/chevron-left.svg index 018f8b673..919d877d2 100644 --- a/frontend/app/svg/icons/chevron-left.svg +++ b/frontend/app/svg/icons/chevron-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/frontend/app/svg/icons/chevron-right.svg b/frontend/app/svg/icons/chevron-right.svg index d621289b3..67cb89d1a 100644 --- a/frontend/app/svg/icons/chevron-right.svg +++ b/frontend/app/svg/icons/chevron-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file From ce33f1deb62244ea1f73815cb0c80a1d6123147c Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 9 Mar 2022 19:07:47 +0100 Subject: [PATCH 03/12] feat(ui) - pagination wip --- .../BugFinder/SessionList/SessionList.js | 51 +++++++++++-------- .../LiveSessionList/LiveSessionList.tsx | 34 ++++--------- frontend/app/duck/search.js | 13 +++++ frontend/app/duck/sessions.js | 2 + frontend/app/utils.js | 6 +++ 5 files changed, 61 insertions(+), 45 deletions(-) diff --git a/frontend/app/components/BugFinder/SessionList/SessionList.js b/frontend/app/components/BugFinder/SessionList/SessionList.js index 10db59c5b..e44610e1b 100644 --- a/frontend/app/components/BugFinder/SessionList/SessionList.js +++ b/frontend/app/components/BugFinder/SessionList/SessionList.js @@ -1,13 +1,14 @@ import { connect } from 'react-redux'; -import { Loader, NoContent, Button, LoadMoreButton } from 'UI'; +import { Loader, NoContent, Button, LoadMoreButton, Pagination } from 'UI'; import { applyFilter, addAttribute, addEvent } from 'Duck/filters'; -import { fetchSessions, addFilterByKeyAndValue } from 'Duck/search'; +import { fetchSessions, addFilterByKeyAndValue, updateCurrentPage } from 'Duck/search'; 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 = 10; +const PER_PAGE = 5; const AUTOREFRESH_INTERVAL = 3 * 60 * 1000; var timeoutId; @@ -20,12 +21,14 @@ var timeoutId; total: state.getIn([ 'sessions', 'total' ]), filters: state.getIn([ 'search', 'instance', 'filters' ]), metaList: state.getIn(['customFields', 'list']).map(i => i.key), + currentPage: state.getIn([ 'search', 'currentPage' ]), }), { applyFilter, addAttribute, addEvent, fetchSessions, addFilterByKeyAndValue, + updateCurrentPage, }) export default class SessionList extends React.PureComponent { state = { @@ -76,6 +79,8 @@ export default class SessionList extends React.PureComponent { clearTimeout(timeoutId) } + + renderActiveTabContent(list) { const { loading, @@ -84,6 +89,7 @@ export default class SessionList extends React.PureComponent { allList, activeTab, metaList, + currentPage, } = this.props; const _filterKeys = filters.map(i => i.key); const hasUserFilter = _filterKeys.includes(FilterKey.USERID) || _filterKeys.includes(FilterKey.USERANONYMOUSID); @@ -93,28 +99,28 @@ export default class SessionList extends React.PureComponent { return ( -
Please try changing your search parameters.
- {allList.size > 0 && ( -
- However, we found other sessions based on your search parameters. -
- +
+
Please try changing your search parameters.
+ {allList.size > 0 && ( +
+ However, we found other sessions based on your search parameters. +
+ +
-
- )} -
+ )} +
} > - { list.take(displayedCount).map(session => ( + { sliceListPerPage(list, currentPage, PER_PAGE).map(session => ( ))} - this.props.updateCurrentPage(page)} + /> + {/* Try being a bit more specific by setting a specific time frame or simply use different filters
} - /> + /> */} ); } diff --git a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx index c2f3c1b3b..16aa8772c 100644 --- a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx +++ b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx @@ -12,7 +12,7 @@ import { addFilterByKeyAndValue, updateCurrentPage, updateSort } from 'Duck/live import DropdownPlain from 'Shared/DropdownPlain'; import SortOrderButton from 'Shared/SortOrderButton'; import { TimezoneDropdown } from 'UI'; -import { capitalize } from 'App/utils'; +import { capitalize, sliceListPerPage } from 'App/utils'; import LiveSessionReloadButton from 'Shared/LiveSessionReloadButton'; const AUTOREFRESH_INTERVAL = .5 * 60 * 1000 @@ -107,12 +107,6 @@ function LiveSessionList(props: Props) { }, AUTOREFRESH_INTERVAL); } - const sliceListPerPage = (list, page) => { - const start = page * PER_PAGE; - const end = start + PER_PAGE; - return list.slice(start, end); - } - return (
@@ -140,13 +134,7 @@ function LiveSessionList(props: Props) { props.updateSort({ order: state })} sortOrder={sort.order} />
-
- props.updateCurrentPage(page)} - /> -
+ ))} - {/* */} - {/* null} - /> */} +
+ props.updateCurrentPage(page)} + /> +
diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js index 3d15ae950..15011dafb 100644 --- a/frontend/app/duck/search.js +++ b/frontend/app/duck/search.js @@ -28,6 +28,7 @@ const CLEAR_SEARCH = `${name}/CLEAR_SEARCH`; 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 REFRESH_FILTER_OPTIONS = 'filters/REFRESH_FILTER_OPTIONS'; @@ -49,6 +50,7 @@ const initialState = Map({ instance: new Filter({ filters: [] }), savedSearch: new SavedFilter({}), filterSearchList: {}, + currentPage: 1, }); // Metric - Series - [] - filters @@ -83,6 +85,8 @@ function reducer(state = initialState, action = {}) { return state.set('savedSearch', action.filter); case EDIT_SAVED_SEARCH: return state.mergeIn([ 'savedSearch' ], action.instance); + case UPDATE_CURRENT_PAGE: + return state.set('currentPage', action.page); } return state; } @@ -122,6 +126,8 @@ const reduceThenFetchResource = actionCreator => (...args) => (dispatch, getStat dispatch(actionCreator(...args)); const filter = getState().getIn([ 'search', 'instance']).toData(); filter.filters = filter.filters.map(filterMap); + filter.limit = 5; + filter.page = getState().getIn([ 'search', 'currentPage']); return isRoute(ERRORS_ROUTE, window.location.pathname) ? dispatch(fetchErrorsList(filter)) @@ -268,4 +274,11 @@ export const refreshFilterOptions = () => { return { type: REFRESH_FILTER_OPTIONS } +} + +export function updateCurrentPage(page) { + return { + type: UPDATE_CURRENT_PAGE, + page, + }; } \ No newline at end of file diff --git a/frontend/app/duck/sessions.js b/frontend/app/duck/sessions.js index f3df333c7..7eedce57a 100644 --- a/frontend/app/duck/sessions.js +++ b/frontend/app/duck/sessions.js @@ -60,6 +60,7 @@ const initialState = Map({ funnelPage: Map(), timelinePointer: null, sessionPath: '', + total: 0, }); const reducer = (state = initialState, action = {}) => { @@ -129,6 +130,7 @@ 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') diff --git a/frontend/app/utils.js b/frontend/app/utils.js index ca7c19b4f..5ea05633c 100644 --- a/frontend/app/utils.js +++ b/frontend/app/utils.js @@ -232,4 +232,10 @@ export const isGreaterOrEqualVersion = (version, compareTo) => { const [major, minor, patch] = version.split("-")[0].split('.'); const [majorC, minorC, patchC] = compareTo.split("-")[0].split('.'); return (major > majorC) || (major === majorC && minor > minorC) || (major === majorC && minor === minorC && patch >= patchC); +} + +export const sliceListPerPage = (list, page, perPage = 10) => { + const start = page * perPage; + const end = start + perPage; + return list.slice(start, end); } \ No newline at end of file From 09aca1e61d0d91c41c93502b843999cb7774d593 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 10 Mar 2022 17:26:07 +0100 Subject: [PATCH 04/12] feat(ui) - pagination wip --- .../app/components/BugFinder/BugFinder.js | 10 ++-- .../BugFinder/SessionList/SessionList.js | 18 ++++--- .../SessionList/SessionListHeader.js | 2 +- .../BugFinder/SessionsMenu/SessionsMenu.js | 50 +++---------------- .../components/ui/Pagination/Pagination.tsx | 9 +++- frontend/app/constants/filterOptions.js | 26 +++++----- frontend/app/duck/search.js | 37 ++++++++++---- frontend/app/duck/sessions.js | 2 - frontend/app/styles/colors-autogen.css | 32 +++++++++++- frontend/app/types/filter/filterType.ts | 15 ++++++ frontend/app/utils.js | 1 + frontend/scripts/colors.js | 4 +- 12 files changed, 123 insertions(+), 83 deletions(-) diff --git a/frontend/app/components/BugFinder/BugFinder.js b/frontend/app/components/BugFinder/BugFinder.js index 6d30359f1..326a1e78e 100644 --- a/frontend/app/components/BugFinder/BugFinder.js +++ b/frontend/app/components/BugFinder/BugFinder.js @@ -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 ( diff --git a/frontend/app/components/BugFinder/SessionList/SessionList.js b/frontend/app/components/BugFinder/SessionList/SessionList.js index e44610e1b..a58d483ae 100644 --- a/frontend/app/components/BugFinder/SessionList/SessionList.js +++ b/frontend/app/components/BugFinder/SessionList/SessionList.js @@ -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 { } > - { sliceListPerPage(list, currentPage, PER_PAGE).map(session => ( + { list.map(session => ( ))} - this.props.updateCurrentPage(page)} - /> +
+ this.props.updateCurrentPage(page)} + /> +
{/* ({ - activeTab: state.getIn([ 'sessions', 'activeTab' ]), + activeTab: state.getIn([ 'search', 'activeTab' ]), }), { applyFilter })(SessionListHeader); diff --git a/frontend/app/components/BugFinder/SessionsMenu/SessionsMenu.js b/frontend/app/components/BugFinder/SessionsMenu/SessionsMenu.js index be98a28cd..fa0594316 100644 --- a/frontend/app/components/BugFinder/SessionsMenu/SessionsMenu.js +++ b/frontend/app/components/BugFinder/SessionsMenu/SessionsMenu.js @@ -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 => ( onMenuItemClick(item)} /> ))} - {/*
-
- -
Assist
- { activeTab.type === 'live' && ( -
!sessionsLoading && props.fetchSessionList(filters.toJS())} - > - { sessionsLoading ? : } -
- )} -
- } - iconName="person" - active={activeTab.type === 'live'} - onClick={() => onMenuItemClick({ name: 'Assist', type: 'live' })} - /> - -
*/} -
({ - 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); diff --git a/frontend/app/components/ui/Pagination/Pagination.tsx b/frontend/app/components/ui/Pagination/Pagination.tsx index 11ae24c53..5e1b422d3 100644 --- a/frontend/app/components/ui/Pagination/Pagination.tsx +++ b/frontend/app/components/ui/Pagination/Pagination.tsx @@ -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 (
{/* void - limit?: number + limit?: number } export default function Pagination(props: Props) { const { page, totalPages, onPageChange, limit = 5 } = props; - const [currentPage, setCurrentPage] = React.useState(page); React.useMemo( () => setCurrentPage(page), [page], ); + const debounceChange = React.useCallback(debounce(onPageChange, 1000), []); + const changePage = (page: number) => { if (page > 0 && page <= totalPages) { - onPageChange(page); setCurrentPage(page); + debounceChange(page); } } - + + const isFirstPage = currentPage === 1; + const isLastPage = currentPage === totalPages; return (
Page of {totalPages}
) From 45857a804b1197e60236640c6b1601df91bacb4b Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 10 Mar 2022 18:15:27 +0100 Subject: [PATCH 06/12] feat(ui) - pagination tooltips :) --- .../components/ui/Pagination/Pagination.tsx | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/frontend/app/components/ui/Pagination/Pagination.tsx b/frontend/app/components/ui/Pagination/Pagination.tsx index 4a8e3f1d5..851b35161 100644 --- a/frontend/app/components/ui/Pagination/Pagination.tsx +++ b/frontend/app/components/ui/Pagination/Pagination.tsx @@ -2,6 +2,7 @@ import React from 'react' import { Icon } from 'UI' import cn from 'classnames' import { debounce } from 'App/utils'; +import { Tooltip } from 'react-tippy'; interface Props { page: number totalPages: number @@ -29,13 +30,21 @@ export default function Pagination(props: Props) { const isLastPage = currentPage === totalPages; return (
- + + Page of {totalPages} - + +
) } From 7e29cde06fbf9228bae69d910efdedaa9ef8b681 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 10 Mar 2022 18:22:19 +0100 Subject: [PATCH 07/12] feat(ui) - pagination limit set --- frontend/app/components/BugFinder/SessionList/SessionList.js | 3 ++- .../components/shared/LiveSessionList/LiveSessionList.tsx | 3 ++- frontend/app/components/ui/Pagination/Pagination.tsx | 5 +++-- frontend/app/duck/search.js | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/app/components/BugFinder/SessionList/SessionList.js b/frontend/app/components/BugFinder/SessionList/SessionList.js index 5a76dcffa..858e9cb30 100644 --- a/frontend/app/components/BugFinder/SessionList/SessionList.js +++ b/frontend/app/components/BugFinder/SessionList/SessionList.js @@ -7,7 +7,7 @@ import SessionListHeader from './SessionListHeader'; import { FilterKey } from 'Types/filter/filterType'; const ALL = 'all'; -const PER_PAGE = 5; +const PER_PAGE = 10; const AUTOREFRESH_INTERVAL = 3 * 60 * 1000; var timeoutId; @@ -136,6 +136,7 @@ export default class SessionList extends React.PureComponent { totalPages={Math.ceil(total / PER_PAGE)} onPageChange={(page) => this.props.updateCurrentPage(page)} limit={PER_PAGE} + debounceRequest={1000} />
{/* props.updateCurrentPage(page)} + limit={PER_PAGE} />
diff --git a/frontend/app/components/ui/Pagination/Pagination.tsx b/frontend/app/components/ui/Pagination/Pagination.tsx index 851b35161..0e552ea69 100644 --- a/frontend/app/components/ui/Pagination/Pagination.tsx +++ b/frontend/app/components/ui/Pagination/Pagination.tsx @@ -8,16 +8,17 @@ interface Props { totalPages: number onPageChange: (page: number) => void limit?: number + debounceRequest?: number } export default function Pagination(props: Props) { - const { page, totalPages, onPageChange, limit = 5 } = props; + const { page, totalPages, onPageChange, limit = 5, debounceRequest = 0 } = props; const [currentPage, setCurrentPage] = React.useState(page); React.useMemo( () => setCurrentPage(page), [page], ); - const debounceChange = React.useCallback(debounce(onPageChange, 1000), []); + const debounceChange = React.useCallback(debounce(onPageChange, debounceRequest), []); const changePage = (page: number) => { if (page > 0 && page <= totalPages) { diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js index dac47f6b7..f4e480b48 100644 --- a/frontend/app/duck/search.js +++ b/frontend/app/duck/search.js @@ -142,7 +142,7 @@ const reduceThenFetchResource = actionCreator => (...args) => (dispatch, getStat } filter.filters = filter.filters.map(filterMap); - filter.limit = 5; + filter.limit = 10; filter.page = getState().getIn([ 'search', 'currentPage']); return isRoute(ERRORS_ROUTE, window.location.pathname) From 7168e660cc8fa66a46a26667d15a7a00c49cfaad Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 15 Mar 2022 17:43:06 +0100 Subject: [PATCH 08/12] feat(ui) - errors pagination --- frontend/app/components/Errors/Errors.js | 72 +++------ frontend/app/components/Errors/List/List.js | 139 ++++++++++-------- .../ui/DropdownPlain/DropdownPlain.js | 2 +- frontend/app/duck/errors.js | 72 ++++++--- frontend/app/duck/search.js | 2 +- frontend/app/types/errorInfo.js | 1 + frontend/app/utils.js | 1 - 7 files changed, 156 insertions(+), 133 deletions(-) diff --git a/frontend/app/components/Errors/Errors.js b/frontend/app/components/Errors/Errors.js index 4eb671cf5..c29a5f200 100644 --- a/frontend/app/components/Errors/Errors.js +++ b/frontend/app/components/Errors/Errors.js @@ -1,23 +1,18 @@ import { connect } from 'react-redux'; import withSiteIdRouter from 'HOCs/withSiteIdRouter'; import withPermissions from 'HOCs/withPermissions' -import { UNRESOLVED, RESOLVED, IGNORED } from "Types/errorInfo"; -import { getRE } from 'App/utils'; -import { fetchBookmarks } from "Duck/errors"; +import { UNRESOLVED, RESOLVED, IGNORED, BOOKMARK } from "Types/errorInfo"; +import { fetchBookmarks, editOptions } from "Duck/errors"; import { applyFilter } from 'Duck/filters'; import { fetchList as fetchSlackList } from 'Duck/integrations/slack'; import { errors as errorsRoute, isRoute } from "App/routes"; -import EventFilter from 'Components/BugFinder/EventFilter'; import DateRange from 'Components/BugFinder/DateRange'; import withPageTitle from 'HOCs/withPageTitle'; -import { SavedSearchList } from 'UI'; - import List from './List/List'; import ErrorInfo from './Error/ErrorInfo'; import Header from './Header'; import SideMenuSection from './SideMenu/SideMenuSection'; -import SideMenuHeader from './SideMenu/SideMenuHeader'; import SideMenuDividedItem from './SideMenu/SideMenuDividedItem'; const ERRORS_ROUTE = errorsRoute(); @@ -39,44 +34,26 @@ function getStatusLabel(status) { @withSiteIdRouter @connect(state => ({ list: state.getIn([ "errors", "list" ]), + status: state.getIn([ "errors", "options", "status" ]), }), { fetchBookmarks, applyFilter, fetchSlackList, + editOptions, }) @withPageTitle("Errors - OpenReplay") export default class Errors extends React.PureComponent { - state = { - status: UNRESOLVED, - bookmarksActive: false, - currentList: this.props.list.filter(e => e.status === UNRESOLVED), - filter: '', + constructor(props) { + super(props) + this.state = { + filter: '', + } } componentDidMount() { this.props.fetchSlackList(); // Delete after implementing cache } - onFilterChange = ({ target: { value } }) => this.setState({ filter: value }) - - componentDidUpdate(prevProps, prevState) { - const { bookmarksActive, status, filter } = this.state; - const { list } = this.props; - if (prevProps.list !== list - || prevState.status !== status - || prevState.bookmarksActive !== bookmarksActive - || prevState.filter !== filter) { - const unfiltered = bookmarksActive - ? list - : list.filter(e => e.status === status); - const filterRE = getRE(filter); - this.setState({ - currentList: unfiltered - .filter(e => filterRE.test(e.name) || filterRE.test(e.message)), - }) - } - } - ensureErrorsPage() { const { history } = this.props; if (!isRoute(ERRORS_ROUTE, history.location.pathname)) { @@ -85,22 +62,11 @@ export default class Errors extends React.PureComponent { } onStatusItemClick = ({ key }) => { - if (this.state.bookmarksActive) { - this.props.applyFilter(); - } - this.setState({ - status: key, - bookmarksActive: false, - }); - this.ensureErrorsPage(); + this.props.editOptions({ status: key }); } onBookmarksClick = () => { - this.setState({ - bookmarksActive: true, - }); - this.props.fetchBookmarks(); - this.ensureErrorsPage(); + this.props.editOptions({ status: BOOKMARK }); } @@ -110,8 +76,9 @@ export default class Errors extends React.PureComponent { match: { params: { errorId } }, + status, + list, } = this.props; - const { status, bookmarksActive, currentList } = this.state; return (
@@ -137,14 +104,14 @@ export default class Errors extends React.PureComponent { icon: "ban", label: getStatusLabel(IGNORED), active: status === IGNORED, - } + } ]} />
@@ -154,8 +121,8 @@ export default class Errors extends React.PureComponent { <>
Seen in @@ -164,12 +131,11 @@ export default class Errors extends React.PureComponent {
: - + }
diff --git a/frontend/app/components/Errors/List/List.js b/frontend/app/components/Errors/List/List.js index cb0ffd55a..2492782c8 100644 --- a/frontend/app/components/Errors/List/List.js +++ b/frontend/app/components/Errors/List/List.js @@ -1,53 +1,62 @@ import cn from 'classnames'; import { connect } from 'react-redux'; import { Set, List as ImmutableList } from "immutable"; -import { NoContent, Loader, Checkbox, LoadMoreButton, IconButton, Input, DropdownPlain } from 'UI'; -import { merge, resolve, unresolve, ignore, updateCurrentPage } from "Duck/errors"; +import { NoContent, Loader, Checkbox, LoadMoreButton, IconButton, Input, DropdownPlain, Pagination } from 'UI'; +import { merge, resolve, unresolve, ignore, updateCurrentPage, editOptions } from "Duck/errors"; import { applyFilter } from 'Duck/filters'; import { IGNORED, RESOLVED, UNRESOLVED } from 'Types/errorInfo'; import SortDropdown from 'Components/BugFinder/Filters/SortDropdown'; import Divider from 'Components/Errors/ui/Divider'; import ListItem from './ListItem/ListItem'; +import { debounce } from 'App/utils'; const PER_PAGE = 5; -const DEFAULT_SORT = 'lastOccurrence'; -const DEFAULT_ORDER = 'desc'; const sortOptionsMap = { 'lastOccurrence-desc': 'Last Occurrence', - 'firstOccurrence-desc': 'First Occurrence', - 'sessions-asc': 'Sessions Ascending', - 'sessions-desc': 'Sessions Descending', - 'users-asc': 'Users Ascending', - 'users-desc': 'Users Descending', + 'firstOccurrence-desc': 'First Occurrence', + 'sessions-asc': 'Sessions Ascending', + 'sessions-desc': 'Sessions Descending', + 'users-asc': 'Users Ascending', + 'users-desc': 'Users Descending', }; const sortOptions = Object.entries(sortOptionsMap) .map(([ value, text ]) => ({ value, text })); - @connect(state => ({ loading: state.getIn([ "errors", "loading" ]), resolveToggleLoading: state.getIn(["errors", "resolve", "loading"]) || state.getIn(["errors", "unresolve", "loading"]), ignoreLoading: state.getIn([ "errors", "ignore", "loading" ]), mergeLoading: state.getIn([ "errors", "merge", "loading" ]), - currentPage: state.getIn(["errors", "currentPage"]), + currentPage: state.getIn(["errors", "currentPage"]), + total: state.getIn([ 'errors', 'totalCount' ]), + sort: state.getIn([ 'errors', 'options', 'sort' ]), + order: state.getIn([ 'errors', 'options', 'order' ]), + query: state.getIn([ "errors", "options", "query" ]), }), { merge, resolve, unresolve, ignore, applyFilter, - updateCurrentPage, + updateCurrentPage, + editOptions, }) export default class List extends React.PureComponent { - state = { - checkedAll: false, - checkedIds: Set(), - sort: {} + constructor(props) { + super(props) + this.state = { + checkedAll: false, + checkedIds: Set(), + query: props.query, + } + this.debounceFetch = debounce(this.props.editOptions, 1000); } - + componentDidMount() { - this.props.applyFilter({ sort: DEFAULT_SORT, order: DEFAULT_ORDER, events: ImmutableList(), filters: ImmutableList() }); + if (this.props.list.size === 0) { + this.props.applyFilter({ }); + } } check = ({ errorId }) => { @@ -111,8 +120,14 @@ export default class List extends React.PureComponent { writeOption = (e, { name, value }) => { const [ sort, order ] = value.split('-'); - const sign = order === 'desc' ? -1 : 1; - this.setState({ sort: { sort, order }}) + if (name === 'sort') { + this.props.editOptions({ sort, order }); + } + } + + onQueryChange = (e, { value }) => { + this.setState({ query: value }); + this.debounceFetch({ query: value }); } render() { @@ -123,19 +138,18 @@ export default class List extends React.PureComponent { ignoreLoading, resolveToggleLoading, mergeLoading, - onFilterChange, - currentPage, + currentPage, + total, + sort, + order, } = this.props; const { checkedAll, checkedIds, - sort + query, } = this.state; const someLoading = loading || ignoreLoading || resolveToggleLoading || mergeLoading; const currentCheckedIds = this.currentCheckedIds(); - const displayedCount = Math.min(currentPage * PER_PAGE, list.size); - let _list = sort.sort ? list.sortBy(i => i[sort.sort]) : list; - _list = sort.order === 'desc' ? _list.reverse() : _list; return (
@@ -182,33 +196,35 @@ export default class List extends React.PureComponent { }
- Sort By + Sort By - + -
- - - - - { _list.take(displayedCount).map(e => - <> + className="input-small ml-3" + placeholder="Filter by Name or Message" + icon="search" + iconPosition="left" + name="filter" + onChange={ this.onQueryChange } + value={query} + /> + + + + + + { list.map(e => +
- +
)} - -
-
+
+ this.props.updateCurrentPage(page)} + limit={PER_PAGE} + debounceRequest={500} + /> +
+
+
); } diff --git a/frontend/app/components/ui/DropdownPlain/DropdownPlain.js b/frontend/app/components/ui/DropdownPlain/DropdownPlain.js index 389b75b93..8f11a14fb 100644 --- a/frontend/app/components/ui/DropdownPlain/DropdownPlain.js +++ b/frontend/app/components/ui/DropdownPlain/DropdownPlain.js @@ -21,7 +21,7 @@ function DropdownPlain({ name, label, options, onChange, defaultValue, wrapperSt options={ options } onChange={ onChange } defaultValue={ defaultValue || options[ 0 ].value } - icon={null} + // icon={null} disabled={disabled} icon={ } /> diff --git a/frontend/app/duck/errors.js b/frontend/app/duck/errors.js index 9e7b552f2..1b099d6d5 100644 --- a/frontend/app/duck/errors.js +++ b/frontend/app/duck/errors.js @@ -1,13 +1,18 @@ import { List, Map } from 'immutable'; import { clean as cleanParams } from 'App/api_client'; -import ErrorInfo, { RESOLVED, UNRESOLVED, IGNORED } from 'Types/errorInfo'; +import ErrorInfo, { RESOLVED, UNRESOLVED, IGNORED, BOOKMARK } from 'Types/errorInfo'; import { createFetch, fetchListType, fetchType } from './funcTools/crud'; import { createRequestReducer, ROOT_KEY } from './funcTools/request'; import { array, request, success, failure, createListUpdater, mergeReducers } from './funcTools/tools'; +import { reduceThenFetchResource } from './search' const name = "error"; const idKey = "errorId"; +const PER_PAGE = 5; +const DEFAULT_SORT = 'lastOccurrence'; +const DEFAULT_ORDER = 'desc'; +const EDIT_OPTIONS = `${name}/EDIT_OPTIONS`; const FETCH_LIST = fetchListType(name); const FETCH = fetchType(name); const FETCH_NEW_ERRORS_COUNT = fetchType('errors/FETCH_NEW_ERRORS_COUNT'); @@ -18,6 +23,7 @@ const MERGE = "errors/MERGE"; const TOGGLE_FAVORITE = "errors/TOGGLE_FAVORITE"; const FETCH_TRACE = "errors/FETCH_TRACE"; const UPDATE_CURRENT_PAGE = "errors/UPDATE_CURRENT_PAGE"; +const UPDATE_KEY = `${name}/UPDATE_KEY`; function chartWrapper(chart = []) { return chart.map(point => ({ ...point, count: Math.max(point.count, 0) })); @@ -35,13 +41,23 @@ const initialState = Map({ instanceTrace: List(), stats: Map(), sourcemapUploaded: true, - currentPage: 1, + currentPage: 1, + options: Map({ + sort: DEFAULT_SORT, + order: DEFAULT_ORDER, + status: UNRESOLVED, + query: '', + }), + // sort: DEFAULT_SORT, + // order: DEFAULT_ORDER, }); function reducer(state = initialState, action = {}) { let updError; switch (action.type) { + case EDIT_OPTIONS: + return state.mergeIn(["options"], action.instance); case success(FETCH): return state.set("instance", ErrorInfo(action.data)); case success(FETCH_TRACE): @@ -69,8 +85,10 @@ function reducer(state = initialState, action = {}) { return state.update("list", list => list.filter(e => !ids.includes(e.errorId))); case success(FETCH_NEW_ERRORS_COUNT): return state.set('stats', action.data); - case UPDATE_CURRENT_PAGE: - return state.set('currentPage', action.page); + case UPDATE_KEY: + return state.set(action.key, action.value); + case UPDATE_CURRENT_PAGE: + return state.set('currentPage', action.page); } return state; } @@ -106,14 +124,31 @@ export function fetchTrace(id) { } } -export function fetchList(params = {}, clear = false) { - return { - types: array(FETCH_LIST), - call: client => client.post('/errors/search', params), - clear, - params: cleanParams(params), - }; -} +export const fetchList = (params = {}, clear = false) => (dispatch, getState) => { + params.page = getState().getIn(['errors', 'currentPage']); + params.limit = PER_PAGE; + + const options = getState().getIn(['errors', 'options']); + if (options.get("status") === BOOKMARK) { + options.bookmarked = true; + } + + return dispatch({ + types: array(FETCH_LIST), + call: client => client.post('/errors/search', { ...params, ...options }), + clear, + params: cleanParams(params), + }); +}; + +// export function fetchList(params = {}, clear = false) { +// return { +// types: array(FETCH_LIST), +// call: client => client.post('/errors/search', params), +// clear, +// params: cleanParams(params), +// }; +// } export function fetchBookmarks() { return { @@ -169,9 +204,12 @@ export function fetchNewErrorsCount(params = {}) { } } -export function updateCurrentPage(page) { - return { - type: 'errors/UPDATE_CURRENT_PAGE', +export const updateCurrentPage = reduceThenFetchResource((page) => ({ + type: UPDATE_CURRENT_PAGE, page, - }; -} +})); + +export const editOptions = reduceThenFetchResource((instance) => ({ + type: EDIT_OPTIONS, + instance +})); \ No newline at end of file diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js index f4e480b48..9106227bb 100644 --- a/frontend/app/duck/search.js +++ b/frontend/app/duck/search.js @@ -126,7 +126,7 @@ export const filterMap = ({category, value, key, operator, sourceOperator, sourc filters: filters ? filters.map(filterMap) : [], }); -const reduceThenFetchResource = actionCreator => (...args) => (dispatch, getState) => { +export const reduceThenFetchResource = actionCreator => (...args) => (dispatch, getState) => { dispatch(actionCreator(...args)); const filter = getState().getIn([ 'search', 'instance']).toData(); diff --git a/frontend/app/types/errorInfo.js b/frontend/app/types/errorInfo.js index efcb5154e..364fa8e65 100644 --- a/frontend/app/types/errorInfo.js +++ b/frontend/app/types/errorInfo.js @@ -5,6 +5,7 @@ import Session from './session'; export const RESOLVED = "resolved"; export const UNRESOLVED = "unresolved"; export const IGNORED = "ignored"; +export const BOOKMARK = "bookmark"; function getStck0InfoString(stack) { diff --git a/frontend/app/utils.js b/frontend/app/utils.js index 795f02d49..5ea05633c 100644 --- a/frontend/app/utils.js +++ b/frontend/app/utils.js @@ -237,6 +237,5 @@ 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); } \ No newline at end of file From 4946dd2036b2f3385539a997c39f1195e38ab185 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 15 Mar 2022 17:44:09 +0100 Subject: [PATCH 09/12] feat(ui) - errors pagination --- frontend/app/components/Errors/List/List.js | 2 +- frontend/app/duck/errors.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app/components/Errors/List/List.js b/frontend/app/components/Errors/List/List.js index 2492782c8..5fb8e49f6 100644 --- a/frontend/app/components/Errors/List/List.js +++ b/frontend/app/components/Errors/List/List.js @@ -10,7 +10,7 @@ import Divider from 'Components/Errors/ui/Divider'; import ListItem from './ListItem/ListItem'; import { debounce } from 'App/utils'; -const PER_PAGE = 5; +const PER_PAGE = 10; const sortOptionsMap = { 'lastOccurrence-desc': 'Last Occurrence', 'firstOccurrence-desc': 'First Occurrence', diff --git a/frontend/app/duck/errors.js b/frontend/app/duck/errors.js index 1b099d6d5..0d2dede92 100644 --- a/frontend/app/duck/errors.js +++ b/frontend/app/duck/errors.js @@ -8,7 +8,7 @@ import { reduceThenFetchResource } from './search' const name = "error"; const idKey = "errorId"; -const PER_PAGE = 5; +const PER_PAGE = 10; const DEFAULT_SORT = 'lastOccurrence'; const DEFAULT_ORDER = 'desc'; From 25239486163dbc4e7a1511dcde409d3207314009 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 15 Mar 2022 19:12:15 +0100 Subject: [PATCH 10/12] feat(ui) - errors pagination --- frontend/app/components/Errors/List/List.js | 4 ++-- frontend/app/duck/errors.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/app/components/Errors/List/List.js b/frontend/app/components/Errors/List/List.js index 5fb8e49f6..2fa91c5e5 100644 --- a/frontend/app/components/Errors/List/List.js +++ b/frontend/app/components/Errors/List/List.js @@ -12,8 +12,8 @@ import { debounce } from 'App/utils'; const PER_PAGE = 10; const sortOptionsMap = { - 'lastOccurrence-desc': 'Last Occurrence', - 'firstOccurrence-desc': 'First Occurrence', + 'occurrence-desc': 'Last Occurrence', + 'occurrence-desc': 'First Occurrence', 'sessions-asc': 'Sessions Ascending', 'sessions-desc': 'Sessions Descending', 'users-asc': 'Users Ascending', diff --git a/frontend/app/duck/errors.js b/frontend/app/duck/errors.js index 0d2dede92..1f41f823a 100644 --- a/frontend/app/duck/errors.js +++ b/frontend/app/duck/errors.js @@ -9,7 +9,7 @@ import { reduceThenFetchResource } from './search' const name = "error"; const idKey = "errorId"; const PER_PAGE = 10; -const DEFAULT_SORT = 'lastOccurrence'; +const DEFAULT_SORT = 'occurrence'; const DEFAULT_ORDER = 'desc'; const EDIT_OPTIONS = `${name}/EDIT_OPTIONS`; From 0c6f3c9a799483e2636857efa1eafd9c8136db29 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 15 Mar 2022 19:41:08 +0100 Subject: [PATCH 11/12] feat(ui) - errors pagination --- frontend/app/duck/errors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/duck/errors.js b/frontend/app/duck/errors.js index 1f41f823a..ea8179dcd 100644 --- a/frontend/app/duck/errors.js +++ b/frontend/app/duck/errors.js @@ -135,7 +135,7 @@ export const fetchList = (params = {}, clear = false) => (dispatch, getState) => return dispatch({ types: array(FETCH_LIST), - call: client => client.post('/errors/search', { ...params, ...options }), + call: client => client.post('/errors/search', { ...params, ...options.toJS() }), clear, params: cleanParams(params), }); From f17a0b324e85a84b3bb633a231b8d4661253bbf8 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 15 Mar 2022 20:02:22 +0100 Subject: [PATCH 12/12] fix(ui) - metadata --- frontend/app/components/Errors/Errors.js | 4 +++- frontend/app/duck/errors.js | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/app/components/Errors/Errors.js b/frontend/app/components/Errors/Errors.js index c29a5f200..10812558d 100644 --- a/frontend/app/components/Errors/Errors.js +++ b/frontend/app/components/Errors/Errors.js @@ -8,6 +8,7 @@ import { fetchList as fetchSlackList } from 'Duck/integrations/slack'; import { errors as errorsRoute, isRoute } from "App/routes"; import DateRange from 'Components/BugFinder/DateRange'; import withPageTitle from 'HOCs/withPageTitle'; +import cn from 'classnames'; import List from './List/List'; import ErrorInfo from './Error/ErrorInfo'; @@ -78,11 +79,12 @@ export default class Errors extends React.PureComponent { }, status, list, + history, } = this.props; return (
-
+
(dispatch, getState) => params.page = getState().getIn(['errors', 'currentPage']); params.limit = PER_PAGE; - const options = getState().getIn(['errors', 'options']); - if (options.get("status") === BOOKMARK) { + const options = getState().getIn(['errors', 'options']).toJS(); + if (options.status === BOOKMARK) { options.bookmarked = true; + options.status = 'all'; } return dispatch({ types: array(FETCH_LIST), - call: client => client.post('/errors/search', { ...params, ...options.toJS() }), + call: client => client.post('/errors/search', { ...params, ...options }), clear, params: cleanParams(params), });