From 1a3335bb590d221a1ed617f44fd26822683d5932 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 18 Aug 2022 17:23:41 +0200 Subject: [PATCH 1/5] feat(ui) - user sesison list modal changes --- .../app/components/Modal/ModalOverlay.tsx | 2 +- frontend/app/components/Session/Session.js | 6 + .../Session_/EventsBlock/UserCard/UserCard.js | 224 +++++++++--------- .../shared/SessionItem/SessionItem.tsx | 36 +-- .../UserSessionsModal/UserSessionsModal.tsx | 93 ++++++++ .../shared/UserSessionsModal/index.ts | 1 + frontend/app/mstore/index.tsx | 3 + frontend/app/mstore/sessionStore.ts | 79 ++++++ frontend/app/services/SessionService.ts | 17 +- 9 files changed, 334 insertions(+), 127 deletions(-) create mode 100644 frontend/app/components/shared/UserSessionsModal/UserSessionsModal.tsx create mode 100644 frontend/app/components/shared/UserSessionsModal/index.ts create mode 100644 frontend/app/mstore/sessionStore.ts diff --git a/frontend/app/components/Modal/ModalOverlay.tsx b/frontend/app/components/Modal/ModalOverlay.tsx index 398e27f2f..019971565 100644 --- a/frontend/app/components/Modal/ModalOverlay.tsx +++ b/frontend/app/components/Modal/ModalOverlay.tsx @@ -4,7 +4,7 @@ import cn from 'classnames'; function ModalOverlay({ hideModal, children, left = false, right = false }: any) { return ( -
+
{ if (sessionId != null) { fetchSession(sessionId) @@ -31,6 +33,10 @@ function Session({ setInitializing(false) },[ sessionId ]); + useEffect(() => { + sessionStore.resetUserFilter(); + } ,[]) + return ( { - setShowUserSessions(true); - request({ key: !userId ? 'USERANONYMOUSID' : 'USERID', value: userId || userAnonymousId }); - } + const hasUserDetails = !!userId || !!userAnonymousId; + const showSimilarSessions = () => { + setShowUserSessions(true); + request({ key: !userId ? 'USERANONYMOUSID' : 'USERID', value: userId || userAnonymousId }); + }; - const getDimension = (width, height) => { - return width && height ? ( -
- { width || 'x' } { height || 'x' } -
- ) : Resolution N/A; - } + const getDimension = (width, height) => { + return width && height ? ( +
+ {width || 'x'} {height || 'x'} +
+ ) : ( + Resolution N/A + ); + }; - const avatarbgSize = '38px' - return ( -
-
- -
- - { userDisplayName } - + const avatarbgSize = '38px'; + return ( +
+
+ +
+ + + -
- {formatTimeOrDate(startedAt, timezone)} - · - {countries[userCountry]} - · - {userBrowser}, {userOs}, {userDevice} - · - - } label={countries[userCountry]} value={{formatTimeOrDate(startedAt)} } /> - - - - {revId && } -
- )} - position="bottom center" - hoverable - disabled={false} - on="hover" - > - - More - - -
-
-
+
+ {formatTimeOrDate(startedAt, timezone)} + · + {countries[userCountry]} + · + + {userBrowser}, {userOs}, {userDevice} + + · + + } + label={countries[userCountry]} + value={{formatTimeOrDate(startedAt)}} + /> + + + + {revId && } +
+ } + position="bottom center" + hoverable + disabled={false} + on="hover" + > + More + +
+
+
- User Sessions
} isDisplayed={ showUserSessions } content={ showUserSessions && } onClose={ () => showUserSessions ? setShowUserSessions(false) : null } - /> -
- ) + /> */} +
+ ); } -const component = React.memo(connect(state => ({ session: state.getIn([ 'sessions', 'current' ]) }))(UserCard)) +const component = React.memo(connect((state) => ({ session: state.getIn(['sessions', 'current']) }))(UserCard)); export default withRequest({ - initialData: List(), - endpoint: '/metadata/session_search', - dataWrapper: data => Object.values(data), - dataName: 'similarSessions', -})(component) + initialData: List(), + endpoint: '/metadata/session_search', + dataWrapper: (data) => Object.values(data), + dataName: 'similarSessions', +})(component); + +// inner component +function UserName({ name, userId, hash }) { + const { showModal } = useModal(); + const onClick = () => { + showModal(, { right: true }); + }; + return
{}}>{name}
; +} diff --git a/frontend/app/components/shared/SessionItem/SessionItem.tsx b/frontend/app/components/shared/SessionItem/SessionItem.tsx index fa886ab8b..fb99dd33e 100644 --- a/frontend/app/components/shared/SessionItem/SessionItem.tsx +++ b/frontend/app/components/shared/SessionItem/SessionItem.tsx @@ -50,6 +50,7 @@ interface Props { lastPlayedSessionId?: string; live?: boolean; onClick?: any; + compact?: boolean; } function SessionItem(props: RouteComponentProps & Props) { @@ -65,6 +66,7 @@ function SessionItem(props: RouteComponentProps & Props) { metaList = [], lastPlayedSessionId, onClick = null, + compact = false, } = props; const { @@ -107,24 +109,26 @@ function SessionItem(props: RouteComponentProps & Props) {
e.stopPropagation()}>
-
-
- -
-
-
!disableUser && !hasUserFilter && onUserClick(userId, userAnonymousId)} - > - + {!compact && ( +
+
+ +
+
+
!disableUser && !hasUserFilter && onUserClick(userId, userAnonymousId)} + > + +
-
-
+ )} +
diff --git a/frontend/app/components/shared/UserSessionsModal/UserSessionsModal.tsx b/frontend/app/components/shared/UserSessionsModal/UserSessionsModal.tsx new file mode 100644 index 000000000..fac7d1b41 --- /dev/null +++ b/frontend/app/components/shared/UserSessionsModal/UserSessionsModal.tsx @@ -0,0 +1,93 @@ +import React, { useEffect } from 'react'; +import { useStore } from 'App/mstore'; +import Filter from 'Types/filter'; +import { filtersMap } from 'Types/filter/newFilter'; +import { FilterKey } from 'App/types/filter/filterType'; +import { NoContent, Pagination, Loader, Avatar } from 'UI'; +import SessionItem from 'Shared/SessionItem'; +import SelectDateRange from 'Shared/SelectDateRange'; +import Period from 'Types/app/period'; +import { useObserver, observer } from 'mobx-react-lite'; + +const PER_PAGE = 10; +interface Props { + userId: string; + hash: string; + name: string; +} +function UserSessionsModal(props: Props) { + const { userId, hash, name } = props; + const { sessionStore } = useStore(); + const [loading, setLoading] = React.useState(false); + const [data, setData] = React.useState({ sessions: [], total: 0 }); + const filter = useObserver(() => sessionStore.userFilter); + + const onDateChange = (period: any) => { + filter.update('period', period); + }; + + const fetchData = () => { + setLoading(true); + sessionStore + .getSessions(filter) + .then(setData) + .catch(() => { + console.log('error'); + }) + .finally(() => { + setLoading(false); + }); + }; + + useEffect(() => { + const userFilter = { key: FilterKey.USERID, value: [userId], operator: 'is', isEvent: false }; + filter.update('filters', [userFilter]); + }, []); + useEffect(fetchData, [filter.page, filter.startDate, filter.endDate]); + + return ( +
+
+
+ +
+ {name}'s Sessions +
+
+
+ +
+
+ + No recordings found.
}> +
+ + {data.sessions.map((session: any) => ( +
+ +
+ ))} +
+ +
+
+ {/* showing x to x of total sessions */} + Showing {(filter.page - 1) * PER_PAGE + 1} to{' '} + {(filter.page - 1) * PER_PAGE + data.sessions.length} of{' '} + {data.total} sessions. +
+ filter.update('page', page)} + limit={PER_PAGE} + debounceRequest={1000} + /> +
+
+ +
+ ); +} + +export default observer(UserSessionsModal); diff --git a/frontend/app/components/shared/UserSessionsModal/index.ts b/frontend/app/components/shared/UserSessionsModal/index.ts new file mode 100644 index 000000000..c48e3ab5a --- /dev/null +++ b/frontend/app/components/shared/UserSessionsModal/index.ts @@ -0,0 +1 @@ +export { default } from './UserSessionsModal'; \ No newline at end of file diff --git a/frontend/app/mstore/index.tsx b/frontend/app/mstore/index.tsx index 445a174f3..0d7c5ce5d 100644 --- a/frontend/app/mstore/index.tsx +++ b/frontend/app/mstore/index.tsx @@ -10,6 +10,7 @@ import SettingsStore from './settingsStore'; import AuditStore from './auditStore'; import NotificationStore from './notificationStore'; import ErrorStore from './errorStore'; +import SessionStore from './sessionStore'; export class RootStore { dashboardStore: IDashboardStore; @@ -21,6 +22,7 @@ export class RootStore { auditStore: AuditStore; errorStore: ErrorStore; notificationStore: NotificationStore + sessionStore: SessionStore; constructor() { this.dashboardStore = new DashboardStore(); @@ -32,6 +34,7 @@ export class RootStore { this.auditStore = new AuditStore(); this.errorStore = new ErrorStore(); this.notificationStore = new NotificationStore(); + this.sessionStore = new SessionStore(); } initClient() { diff --git a/frontend/app/mstore/sessionStore.ts b/frontend/app/mstore/sessionStore.ts new file mode 100644 index 000000000..e983fa9ed --- /dev/null +++ b/frontend/app/mstore/sessionStore.ts @@ -0,0 +1,79 @@ +import { makeAutoObservable, observable, action } from 'mobx'; +import { sessionService } from 'App/services'; +import { filterMap } from 'Duck/search'; +import Session from './types/session'; +import Record, { LAST_24_HOURS, LAST_7_DAYS } from 'Types/app/period'; + +class UserFilter { + endDate: number = new Date().getTime(); + startDate: number = new Date().getTime() - 24 * 60 * 60 * 1000; + rangeName: string = LAST_7_DAYS; + filters: any = []; + page: number = 1; + limit: number = 10; + period: any = Record({ rangeName: LAST_7_DAYS }); + + constructor() { + makeAutoObservable(this, { + page: observable, + update: action, + }); + } + + update(key: string, value: any) { + this[key] = value; + + if (key === 'period') { + this.startDate = this.period.start; + this.endDate = this.period.end; + } + } + + setFilters(filters: any[]) { + this.filters = filters; + } + + setPage(page: number) { + this.page = page; + } + + toJson() { + return { + endDate: this.period.end, + startDate: this.period.start, + filters: this.filters.map(filterMap), + page: this.page, + limit: this.limit, + }; + } +} + +export default class SessionStore { + userFilter: UserFilter = new UserFilter(); + + constructor() { + makeAutoObservable(this, { + userFilter: observable, + }); + } + + resetUserFilter() { + this.userFilter = new UserFilter(); + } + + getSessions(filter: any): Promise { + return new Promise((resolve, reject) => { + sessionService + .getSessions(filter.toJson()) + .then((response: any) => { + resolve({ + sessions: response.sessions.map((session: any) => new Session().fromJson(session)), + total: response.total, + }); + }) + .catch((error: any) => { + reject(error); + }); + }); + } +} diff --git a/frontend/app/services/SessionService.ts b/frontend/app/services/SessionService.ts index a7940edc1..07c623359 100644 --- a/frontend/app/services/SessionService.ts +++ b/frontend/app/services/SessionService.ts @@ -1,4 +1,5 @@ import APIClient from 'App/api_client'; +import { fetchErrorCheck } from 'App/utils'; export default class SettingsService { private client: APIClient; @@ -16,8 +17,16 @@ export default class SettingsService { } fetchCaptureRate() { - return this.client.get('/sample_rate') - .then(response => response.json()) - .then(response => response.data || 0); + return this.client + .get('/sample_rate') + .then((response) => response.json()) + .then((response) => response.data || 0); } -} \ No newline at end of file + + getSessions(filter: any) { + return this.client + .post('/sessions/search2', filter) + .then(fetchErrorCheck) + .then((response) => response.data || []); + } +} From e02b552adda6238108fd86776f7b3f26bfa51594 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 18 Aug 2022 17:38:30 +0200 Subject: [PATCH 2/5] feat(ui) - user sesison list default range --- frontend/app/mstore/sessionStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/mstore/sessionStore.ts b/frontend/app/mstore/sessionStore.ts index e983fa9ed..98a7061e6 100644 --- a/frontend/app/mstore/sessionStore.ts +++ b/frontend/app/mstore/sessionStore.ts @@ -2,7 +2,7 @@ import { makeAutoObservable, observable, action } from 'mobx'; import { sessionService } from 'App/services'; import { filterMap } from 'Duck/search'; import Session from './types/session'; -import Record, { LAST_24_HOURS, LAST_7_DAYS } from 'Types/app/period'; +import Record, { LAST_7_DAYS } from 'Types/app/period'; class UserFilter { endDate: number = new Date().getTime(); From 775ca53e53ad7f1804356fd0ebbf03f446477322 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 18 Aug 2022 17:40:04 +0200 Subject: [PATCH 3/5] feat(ui) - live session modal position --- .../app/components/Assist/components/AssistTabs/AssistTabs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/components/Assist/components/AssistTabs/AssistTabs.tsx b/frontend/app/components/Assist/components/AssistTabs/AssistTabs.tsx index 93a73c236..2fd09105b 100644 --- a/frontend/app/components/Assist/components/AssistTabs/AssistTabs.tsx +++ b/frontend/app/components/Assist/components/AssistTabs/AssistTabs.tsx @@ -20,7 +20,7 @@ const AssistTabs = (props: Props) => { <>
showModal(, {})} + onClick={() => showModal(, { right: true })} > Active Sessions
From 79457430f70a1cebe2966d7c3eb0d057883de402 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 18 Aug 2022 18:44:51 +0200 Subject: [PATCH 4/5] fix(ui) - reset date range on project change --- frontend/app/duck/search.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js index 2080e1f85..15cfa1a4f 100644 --- a/frontend/app/duck/search.js +++ b/frontend/app/duck/search.js @@ -288,9 +288,9 @@ export function fetchFilterSearch(params) { } export const clearSearch = () => (dispatch, getState) => { - const filter = getState().getIn(['search', 'instance']); + // const filter = getState().getIn(['search', 'instance']); // dispatch(applySavedSearch(new SavedFilter({}))); - dispatch(edit(new Filter({ startDate: filter.startDate, endDate: filter.endDate, rangeValue: filter.rangeValue, filters: [] }))); + dispatch(edit(new Filter({ filters: [] }))); return dispatch({ type: CLEAR_SEARCH, }); From 65f4c6d91c63150a6ad6deee6cd58c4504161c37 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 18 Aug 2022 18:45:51 +0200 Subject: [PATCH 5/5] fix(ui) - reset date range on project change - log --- .../app/components/BugFinder/SessionList/SessionListHeader.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/app/components/BugFinder/SessionList/SessionListHeader.js b/frontend/app/components/BugFinder/SessionList/SessionListHeader.js index afb135099..c6994fdaf 100644 --- a/frontend/app/components/BugFinder/SessionList/SessionListHeader.js +++ b/frontend/app/components/BugFinder/SessionList/SessionListHeader.js @@ -26,7 +26,6 @@ function SessionListHeader({ activeTab, count, applyFilter, filter }) { }, [label]); const { startDate, endDate, rangeValue } = filter; - console.log('startDate', startDate); const period = new Record({ start: startDate, end: endDate, rangeName: rangeValue, timezoneOffset: getTimeZoneOffset() }); const onDateChange = (e) => {