diff --git a/frontend/app/duck/sessions.ts b/frontend/app/duck/sessions.ts index 3fd035974..79298de87 100644 --- a/frontend/app/duck/sessions.ts +++ b/frontend/app/duck/sessions.ts @@ -124,7 +124,7 @@ const reducer = (state = initialState, action: IAction) => { return state.set('filteredEvents', filteredEvents).set('eventsQuery', query); } case FETCH.SUCCESS: { - // TODO: more common.. or TEMP + // TODO: more common.. or TEMP filters', 'appliedFilter const events = action.filter.events; const session = new Session(action.data); @@ -191,17 +191,10 @@ const reducer = (state = initialState, action: IAction) => { }; return state.update('list', (list: Session[]) => list.sort(comparator)).update('favoriteList', (list: Session[]) => list.sort(comparator)); } - case REDEFINE_TARGET: { - // TODO: update for list - const { label, path } = action.target; - return state.updateIn(['current', 'events'], (list) => - list.map((event) => (event.target && event.target.path === path ? event.setIn(['target', 'label'], label) : event)) - ); - } case SET_ACTIVE_TAB: const allList = action.tab.type === 'all' ? state.get('list') : state.get('list').filter((s) => s.issueTypes.includes(action.tab.type)); - return state.set('activeTab', action.tab).set('sessionIds', allList.map(({ sessionId }) => sessionId).toJS()); + return state.set('activeTab', action.tab).set('sessionIds', allList.map(({ sessionId }) => sessionId)); case SET_TIMEZONE: return state.set('timezone', action.timezone); case TOGGLE_CHAT_WINDOW: diff --git a/frontend/app/mstore/sessionStore.ts b/frontend/app/mstore/sessionStore.ts index ef8d1312e..21c54de22 100644 --- a/frontend/app/mstore/sessionStore.ts +++ b/frontend/app/mstore/sessionStore.ts @@ -8,6 +8,8 @@ import ErrorStack from 'Types/session/errorStack'; import { Location, InjectedEvent } from 'Types/session/event' import { getDateRangeFromValue } from "App/dateRange"; import { getRE, setSessionFilter, getSessionFilter, compareJsonObjects, cleanSessionFilters } from 'App/utils'; +import store from 'App/store' +import { Note } from "App/services/NotesService"; class UserFilter { endDate: number = new Date().getTime(); @@ -110,12 +112,13 @@ export default class SessionStore { showChatWindow = false liveSessions: Session[] = [] visitedEvents = [] - insights = [] + insights: any[] = [] insightFilters = defaultDateFilters host = '' funnelPage = {} - timelinePointer = null - sessionPath = {} + /** @Deprecated */ + timelinePointer = {} + sessionPath = {} lastPlayedSessionId: string timeLineTooltip = { time: 0, offset: 0, isVisible: false, timeStr: '' } createNoteTooltip = { time: 0, isVisible: false, isEdit: false, note: null } @@ -174,7 +177,43 @@ export default class SessionStore { this.total = data.total; this.sessionIds = data.sessions.map(s => s.sessionId); this.favoriteList = list.filter(s => s.favorite); - } catch(e) { + } catch (e) { + console.error(e) + } + } + + async fetchSessionInfo(sessionId: string, isLive = false) { + try { + const { events } = store.getState().getIn(['filters', 'appliedFilter']); + const data = await sessionService.getSessionInfo(sessionId, isLive) + const session = new Session(data) + + const matching: number[] = []; + const visitedEvents: Location[] = []; + const tmpMap: Set = new Set(); + + session.events.forEach((event) => { + if (event.type === 'LOCATION' && !tmpMap.has(event.url)) { + tmpMap.add(event.url); + visitedEvents.push(event); + } + }); + + + (events as {}[]).forEach(({ key, operator, value }: any) => { + session.events.forEach((e, index) => { + if (key === e.type) { + const val = e.type === 'LOCATION' ? e.url : e.value; + if (operator === 'is' && value === val) { + matching.push(index); + } + if (operator === 'contains' && val.includes(value)) { + matching.push(index); + } + } + }); + }); + } catch (e) { console.error(e) } } @@ -194,7 +233,7 @@ export default class SessionStore { const data = await sessionService.getAutoplayList(params); const list = [...this.sessionIds, ...data.map(s => s.sessionId)] this.sessionIds = list.filter((id, ind) => list.indexOf(id) === ind); - } catch(e) { + } catch (e) { console.error(e) } } @@ -221,6 +260,147 @@ export default class SessionStore { ) : null; this.filteredEvents = filteredEvents - this.eventsQuery = query + this.eventsQuery = query } + + async toggleFavorite(id: string) { + try { + const r = await sessionService.toggleFavorite(id) + if (r.success) { + const list = this.list; + const current = this.current; + const sessionIdx = list.findIndex(({ sessionId }) => sessionId === id); + const session = list[sessionIdx] + const wasInFavorite = this.favoriteList.findIndex(({ sessionId }) => sessionId === id) > -1; + + if (session && !wasInFavorite) { + session.favorite = true + this.list[sessionIdx] = session + } + if (current.sessionId === id) { + this.current.favorite = !wasInFavorite + } + + if (session) { + if (wasInFavorite) { + this.favoriteList = this.favoriteList.filter(({ sessionId }) => sessionId !== id) + } else { + this.favoriteList.push(session) + } + } + } else { + console.error(r) + } + } catch (e) { + console.error(e) + } + } + + sortSessions(sortKey: string, sign: number) { + const comparator = (s1: Session, s2: Session) => { + // @ts-ignore + let diff = s1[sortKey] - s2[sortKey]; + diff = diff === 0 ? s1.startedAt - s2.startedAt : diff; + return sign * diff; + }; + + this.list = this.list.sort(comparator) + this.favoriteList = this.favoriteList.sort(comparator) + return; + } + + setActiveTab(tab: { type: string }) { + const list = tab.type === 'all' + ? this.list : this.list.filter(s => s.issueTypes.includes(tab.type)) + + // @ts-ignore + this.activeTab = tab + this.sessionIds = list.map(s => s.sessionId) + } + + setTimezone(tz: string) { + this.timezone = tz; + } + + toggleChatWindow(isActive: boolean) { + this.showChatWindow = isActive + } + + async fetchInsights(filters = {}) { + try { + const data = await sessionService.getClickMap(filters) + + this.insights = data + } catch (e) { + console.error(e) + } + } + + setFunnelPage(page = {}) { + this.funnelPage = page || false + } + + /* @deprecated */ + setTimelinePointer(pointer: {}) { + this.timelinePointer = pointer + } + + setTimelineTooltip(tp: { time: number, offset: number, isVisible: boolean, timeStr: string }) { + this.timeLineTooltip = tp + } + + setEditNoteTooltip(tp: { time: number, isVisible: boolean, isEdit: boolean, note: any }) { + this.createNoteTooltip = tp + } + + filterOutNote(noteId: string) { + const current = this.current + + current.notesWithEvents = current.notesWithEvents.filter(n => { + if ('noteId' in item) { + return item.noteId !== noteId + } + return true + }) + + this.current = current + } + + addNote(note: Note) { + const current = this.current + + current.notesWithEvents.push(note) + current.notesWithEvents.sort((a,b) => { + const aTs = a.time || a.timestamp + const bTs = b.time || b.timestamp + + return aTs - bTs + }) + + this.current = current + } + + updateNote(note: Note) { + const noteIndex = this.current.notesWithEvents.findIndex(item => { + if ('noteId' in item) { + return item.noteId === note.noteId + } + return false + }) + + this.current.notesWithEvents[noteIndex] = note + } + + setSessionPath(path = {}) { + this.sessionPath = path + } + + setLastPlayed(sessionId: string) { + const list = this.list + const sIndex = list.findIndex((s) => s.sessionId === sessionId) + if (sIndex !== -1) { + this.list[sIndex].viewed = true + } + } + } diff --git a/frontend/app/services/SessionService.ts b/frontend/app/services/SessionService.ts index 60e2a1958..6537a2dbb 100644 --- a/frontend/app/services/SessionService.ts +++ b/frontend/app/services/SessionService.ts @@ -57,11 +57,25 @@ export default class SettingsService { .catch(e => Promise.reject(e)) } - getAutoplayList(params = {}): Promise<{ sessionId: string}[]> { + getAutoplayList(params = {}): Promise<{ sessionId: string }[]> { return this.client .post('/sessions/search/ids', cleanParams(params)) .then(r => r.json()) .then(j => j.data || []) .catch(e => Promise.reject(e)) } + + toggleFavorite(sessionId: string): Promise { + return this.client + .get(`/sessions/${sessionId}/favorite`) + .catch(Promise.reject) + } + + getClickMap(params = {}): Promise { + return this.client + .post('/heatmaps/url', params) + .then(r => r.json()) + .then(j => j.data || []) + .catch(Promise.reject) + } } diff --git a/frontend/app/types/dashboard/callWithErrors.js b/frontend/app/types/dashboard/callWithErrors.js deleted file mode 100644 index 3c3aefd7c..000000000 --- a/frontend/app/types/dashboard/callWithErrors.js +++ /dev/null @@ -1,13 +0,0 @@ -import Record from 'Types/Record'; - -export default Record({ - method: '', - urlHostpath: '', - allRequests: '', - '4xx': '', - '5xx': '' -}, { - // fromJS: pm => ({ - // ...pm, - // }), -}); \ No newline at end of file diff --git a/frontend/app/types/dashboard/domBuildingTime copy.js b/frontend/app/types/dashboard/domBuildingTime copy.js deleted file mode 100644 index 258902f60..000000000 --- a/frontend/app/types/dashboard/domBuildingTime copy.js +++ /dev/null @@ -1,14 +0,0 @@ -import { Record } from 'immutable'; - -const DomBuildingTime = Record({ - avg: undefined, - chart: [], -}); - - -function fromJS(data = {}) { - if (data instanceof DomBuildingTime) return data; - return new DomBuildingTime(data); -} - -export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/helper.js b/frontend/app/types/dashboard/helper.ts similarity index 54% rename from frontend/app/types/dashboard/helper.js rename to frontend/app/types/dashboard/helper.ts index f6e819da8..97a7c17e8 100644 --- a/frontend/app/types/dashboard/helper.js +++ b/frontend/app/types/dashboard/helper.ts @@ -1,30 +1,9 @@ -export const getDayStartAndEndTimestamps = (date) => { - const start = moment(date).startOf('day').valueOf(); - const end = moment(date).endOf('day').valueOf(); - return { start, end }; -}; - -// const getPerformanceDensity = (period) => { -// switch (period) { -// case HALF_AN_HOUR: -// return 30; -// case WEEK: -// return 84; -// case MONTH: -// return 90; -// case DAY: -// return 48; -// default: -// return 48; -// } -// }; - const DAY = 1000 * 60 * 60 * 24; const WEEK = DAY * 8; -const startWithZero = num => (num < 10 ? `0${ num }` : `${ num }`); +const startWithZero = (num: number) => (num < 10 ? `0${ num }` : `${ num }`); const weekdays = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]; -// const months = [ "January", "February" ]; + export const getTimeString = (ts, period) => { const date = new Date(ts); const diff = period.endTimestamp - period.startTimestamp; @@ -41,11 +20,11 @@ export const getTimeString = (ts, period) => { export const getChartFormatter = period => (data = []) => data.map(({ timestamp, ...rest }) => ({ time: getTimeString(timestamp, period), ...rest, timestamp })); -export const getStartAndEndTimestampsByDensity = (current, start, end, density) => { +export const getStartAndEndTimestampsByDensity = (current: number, start: number, end: number, density: number) => { const diff = end - start; const step = Math.floor(diff / density); const currentIndex = Math.floor((current - start) / step); - const startTimestamp = parseInt(start + currentIndex * step); - const endTimestamp = parseInt(startTimestamp + step); + const startTimestamp = start + currentIndex * step; + const endTimestamp = startTimestamp + step; return { startTimestamp, endTimestamp }; }; \ No newline at end of file diff --git a/frontend/app/types/dashboard/index.js b/frontend/app/types/dashboard/index.ts similarity index 96% rename from frontend/app/types/dashboard/index.js rename to frontend/app/types/dashboard/index.ts index 670f44a2c..3e2ff0e74 100644 --- a/frontend/app/types/dashboard/index.js +++ b/frontend/app/types/dashboard/index.ts @@ -1,4 +1,4 @@ -import { Map, List } from 'immutable'; +import { List } from 'immutable'; import Session from 'Types/session'; import { camelCased } from 'App/utils'; @@ -29,7 +29,6 @@ import SlowestDomains from './slowestDomains'; import ResourceLoadingTime from './resourceLoadingTime'; import Image from './image'; -import Resource from './resource'; import Err from './err'; import MissingResource from './missingResource'; @@ -58,7 +57,7 @@ export const WIDGET_LIST = [{ name: "User Activity", description: 'The average user feedback score, average number of visited pages per session and average session duration.', thumb: 'user_activity.png', - dataWrapper: UserActivity, + dataWrapper: data => new UserActivity(data), }, { key: "pageMetrics", name: "Page Metrics", @@ -85,13 +84,6 @@ export const WIDGET_LIST = [{ return i2.avgDuration - i1.avgDuration; }), }, - // { - // key: "errorsTrend", - // name: "Most Impactful Errors", - // description: 'List of errors and exceptions, sorted by the number of impacted sessions.', - // thumb: 'most_Impactful_errors.png', - // dataWrapper: list => List(list).map(Err), - // }, { key: "sessionsFrustration", name: "Recent Frustrations", @@ -114,19 +106,13 @@ export const WIDGET_LIST = [{ type: 'resources', dataWrapper: list => List(list).map(MissingResource), }, - // { - // key: "sessionsPerformance", - // name: "Recent Performance Issues", - // description: "", - // dataWrapper: list => List(list).map(Session), - // } { key: "slowestResources", name: "Slowest Resources", description: 'List of resources that are slowing down your website, sorted by the number of impacted sessions.', thumb: 'na.png', type: 'resources', - dataWrapper: list => List(list).map(SlowestResources) + dataWrapper: list => list.map(res => new SlowestResources(res)) }, { key: "overview", @@ -138,8 +124,6 @@ export const WIDGET_LIST = [{ .map(item => OverviewWidget({ key: camelCased(item.key), ...item.data})) .map(widget => widget.update("chart", getChartFormatter(period))) } - // dataWrapper: (p, period) => List(p) - // .update("chart", getChartFormatter(period)) }, { key: "speedLocation", diff --git a/frontend/app/types/dashboard/resource.js b/frontend/app/types/dashboard/resource.js deleted file mode 100644 index 341ef8e47..000000000 --- a/frontend/app/types/dashboard/resource.js +++ /dev/null @@ -1,16 +0,0 @@ -import Record from 'Types/Record'; - -const getName = (url = '') => url.split('/').filter(part => !!part).pop(); - -export default Record({ - avgDuration: undefined, - sessions: undefined, - chart: [], - url: '', - name: '', -}, { - fromJS: (resource) => ({ - ...resource, - name: getName(resource.url), - }) -}); diff --git a/frontend/app/types/dashboard/resource.ts b/frontend/app/types/dashboard/resource.ts new file mode 100644 index 000000000..c35b00765 --- /dev/null +++ b/frontend/app/types/dashboard/resource.ts @@ -0,0 +1,24 @@ +const getName = (url = '') => url.split('/').filter(part => !!part).pop(); + +interface IResource { + avgDuration: number; + sessions: any[]; + chart: any[]; + url: string; + name: string; +} + +export default class Resource { + avgDuration: IResource["avgDuration"]; + sessions: IResource["sessions"]; + chart: IResource["chart"] = []; + url: IResource["url"] = ''; + name: IResource["name"] = ''; + + constructor(data: IResource) { + Object.assign(this, { + ...data, + name: getName(data.url), + }) + } +} \ No newline at end of file diff --git a/frontend/app/types/dashboard/resourceLoadingTime.js b/frontend/app/types/dashboard/resourceLoadingTime.js deleted file mode 100644 index cc23bdae9..000000000 --- a/frontend/app/types/dashboard/resourceLoadingTime.js +++ /dev/null @@ -1,13 +0,0 @@ -import { Record } from 'immutable'; - -const ResourceLoadingTime = Record({ - avg: undefined, - timestamp: undefined -}); - -function fromJS(resourceLoadingTime = {}) { - if (resourceLoadingTime instanceof ResourceLoadingTime) return resourceLoadingTime; - return new ResourceLoadingTime(resourceLoadingTime); -} - -export default fromJS; diff --git a/frontend/app/types/dashboard/resourceLoadingTime.ts b/frontend/app/types/dashboard/resourceLoadingTime.ts new file mode 100644 index 000000000..65a9114a9 --- /dev/null +++ b/frontend/app/types/dashboard/resourceLoadingTime.ts @@ -0,0 +1,21 @@ + +interface IResourceLoadingTime { + avg: number; + timestamp: number; +} + +class ResourceLoadingTime { + avg: IResourceLoadingTime["avg"]; + timestamp: IResourceLoadingTime["timestamp"]; + + constructor(data: IResourceLoadingTime) { + Object.assign(this, data) + } +} + +function fromJS(resourceLoadingTime = {}) { + if (resourceLoadingTime instanceof ResourceLoadingTime) return resourceLoadingTime; + return new ResourceLoadingTime(resourceLoadingTime); +} + +export default fromJS; diff --git a/frontend/app/types/dashboard/responseTime.js b/frontend/app/types/dashboard/responseTime.js deleted file mode 100644 index 961eda42b..000000000 --- a/frontend/app/types/dashboard/responseTime.js +++ /dev/null @@ -1,14 +0,0 @@ -import { Record } from 'immutable'; - -const ResponseTime = Record({ - avg: undefined, - chart: [], -}); - - -function fromJS(data = {}) { - if (data instanceof ResponseTime) return data; - return new ResponseTime(data); -} - -export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/responseTime.ts b/frontend/app/types/dashboard/responseTime.ts new file mode 100644 index 000000000..774440008 --- /dev/null +++ b/frontend/app/types/dashboard/responseTime.ts @@ -0,0 +1,20 @@ +interface IResponseTime { + avg: number; + chart: any[]; +} + +class ResponseTime { + avg: IResponseTime["avg"] + chart: IResponseTime["chart"] = [] + + constructor(data: IResponseTime) { + Object.assign(this, data) + } +} + +function fromJS(data = {}) { + if (data instanceof ResponseTime) return data; + return new ResponseTime(data); +} + +export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/responseTimeDistribution.js b/frontend/app/types/dashboard/responseTimeDistribution.js deleted file mode 100644 index a53f3555f..000000000 --- a/frontend/app/types/dashboard/responseTimeDistribution.js +++ /dev/null @@ -1,17 +0,0 @@ -import { Record } from 'immutable'; - -const ResponseTimeDistribution = Record({ - chart: [], - avg: undefined, - percentiles: [], - extremeValues: [], - total: undefined -}); - - -function fromJS(data = {}) { - if (data instanceof ResponseTimeDistribution) return data; - return new ResponseTimeDistribution(data); -} - -export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/responseTimeDistribution.ts b/frontend/app/types/dashboard/responseTimeDistribution.ts new file mode 100644 index 000000000..76a96a1b2 --- /dev/null +++ b/frontend/app/types/dashboard/responseTimeDistribution.ts @@ -0,0 +1,27 @@ +interface IResponseTimeDistribution { + chart: any[], + avg: number, + percentiles: number[], + extremeValues: number[], + total: number +} + +class ResponseTimeDistribution { + chart: IResponseTimeDistribution["chart"] = [] + avg: IResponseTimeDistribution["avg"] + percentiles: IResponseTimeDistribution["percentiles"] = [] + extremeValues: IResponseTimeDistribution["extremeValues"] = [] + total: IResponseTimeDistribution["total"] + + constructor(data: IResponseTimeDistribution) { + Object.assign(this, data) + } +} + + +function fromJS(data = {}) { + if (data instanceof ResponseTimeDistribution) return data; + return new ResponseTimeDistribution(data); +} + +export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/sessionsImpactedByJSErrors.js b/frontend/app/types/dashboard/sessionsImpactedByJSErrors.js deleted file mode 100644 index c0368744d..000000000 --- a/frontend/app/types/dashboard/sessionsImpactedByJSErrors.js +++ /dev/null @@ -1,14 +0,0 @@ -import { Record } from 'immutable'; - -const SessionsImpactedByJSErrors = Record({ - errorsCount: undefined, - chart: [] -}); - - -function fromJS(data = {}) { - if (data instanceof SessionsImpactedByJSErrors) return data; - return new SessionsImpactedByJSErrors(data); -} - -export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/sessionsImpactedByJSErrors.ts b/frontend/app/types/dashboard/sessionsImpactedByJSErrors.ts new file mode 100644 index 000000000..843ed6b9a --- /dev/null +++ b/frontend/app/types/dashboard/sessionsImpactedByJSErrors.ts @@ -0,0 +1,20 @@ +interface ISessionsImpactedByJSErrors { + errorsCount: number; + chart: any[]; +} + +class SessionsImpactedByJSErrors { + errorsCount: ISessionsImpactedByJSErrors["errorsCount"]; + chart: ISessionsImpactedByJSErrors["chart"]; + + constructor(data: ISessionsImpactedByJSErrors) { + Object.assign(this, data) + } +} + +function fromJS(data = {}) { + if (data instanceof SessionsImpactedByJSErrors) return data; + return new SessionsImpactedByJSErrors(data); +} + +export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/sessionsImpactedBySlowRequests.js b/frontend/app/types/dashboard/sessionsImpactedBySlowRequests.js deleted file mode 100644 index 4d5d5fc61..000000000 --- a/frontend/app/types/dashboard/sessionsImpactedBySlowRequests.js +++ /dev/null @@ -1,14 +0,0 @@ -import { Record } from 'immutable'; - -const SessionsImpactedBySlowRequests = Record({ - avg: undefined, - chart: [], -}); - - -function fromJS(data = {}) { - if (data instanceof SessionsImpactedBySlowRequests) return data; - return new SessionsImpactedBySlowRequests(data); -} - -export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/sessionsImpactedBySlowRequests.ts b/frontend/app/types/dashboard/sessionsImpactedBySlowRequests.ts new file mode 100644 index 000000000..6b905fc26 --- /dev/null +++ b/frontend/app/types/dashboard/sessionsImpactedBySlowRequests.ts @@ -0,0 +1,20 @@ +interface ISessionsSlowRequests { + avg: number; + chart: any[]; +} + +class SessionsImpactedBySlowRequests { + avg: ISessionsSlowRequests["avg"]; + chart: ISessionsSlowRequests["chart"] = []; + + constructor(data: ISessionsSlowRequests) { + Object.assign(this, data) + } +} + +function fromJS(data = {}) { + if (data instanceof SessionsImpactedBySlowRequests) return data; + return new SessionsImpactedBySlowRequests(data); +} + +export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/sessionsPerBrowser.js b/frontend/app/types/dashboard/sessionsPerBrowser.js deleted file mode 100644 index cd8662b13..000000000 --- a/frontend/app/types/dashboard/sessionsPerBrowser.js +++ /dev/null @@ -1,13 +0,0 @@ -import { Record } from 'immutable'; - -const SessionsPerBrowser = Record({ - count: undefined, - chart: [], -}); - -function fromJS(sessionsPerBrowser = {}) { - if (sessionsPerBrowser instanceof SessionsPerBrowser) return sessionsPerBrowser; - return new SessionsPerBrowser({...sessionsPerBrowser, avg: Math.round(sessionsPerBrowser.avg)}); -} - -export default fromJS; diff --git a/frontend/app/types/dashboard/sessionsPerBrowser.ts b/frontend/app/types/dashboard/sessionsPerBrowser.ts new file mode 100644 index 000000000..ec7a08c76 --- /dev/null +++ b/frontend/app/types/dashboard/sessionsPerBrowser.ts @@ -0,0 +1,22 @@ +interface ISessionsPerBrowser { + count?: number, + chart?: any[], + avg: number, +} + +class SessionsPerBrowser { + count: ISessionsPerBrowser["count"] + chart: ISessionsPerBrowser["chart"] = [] + avg: ISessionsPerBrowser["avg"] + + constructor(data: ISessionsPerBrowser) { + Object.assign(this, data) + } +} + +function fromJS(sessionsPerBrowser = {}) { + if (sessionsPerBrowser instanceof SessionsPerBrowser) return sessionsPerBrowser; + return new SessionsPerBrowser({...sessionsPerBrowser, avg: Math.round(sessionsPerBrowser.avg)}); +} + +export default fromJS; diff --git a/frontend/app/types/dashboard/slowestDomains.js b/frontend/app/types/dashboard/slowestDomains.ts similarity index 51% rename from frontend/app/types/dashboard/slowestDomains.js rename to frontend/app/types/dashboard/slowestDomains.ts index fc15ed831..9dbe09870 100644 --- a/frontend/app/types/dashboard/slowestDomains.js +++ b/frontend/app/types/dashboard/slowestDomains.ts @@ -1,9 +1,18 @@ import { Record } from 'immutable'; -const SlowestDomains = Record({ - partition: [], - avg: undefined, -}); +interface ISlowestDomains { + partition?: string[]; + avg: number; +} + +class SlowestDomains { + partition: ISlowestDomains["partition"] = []; + avg: ISlowestDomains["avg"]; + + constructor(data: ISlowestDomains) { + Object.assign(this, data) + } +} function fromJS(slowestDomains = {}) { if (slowestDomains instanceof SlowestDomains) return slowestDomains; diff --git a/frontend/app/types/dashboard/slowestResources.js b/frontend/app/types/dashboard/slowestResources.js deleted file mode 100644 index 54e00984b..000000000 --- a/frontend/app/types/dashboard/slowestResources.js +++ /dev/null @@ -1,21 +0,0 @@ -import Record from 'Types/Record'; -import { fileType, fileName } from 'App/utils'; - -const validTypes = ['jpg', 'jpeg', 'js', 'css', 'woff', 'css', 'png', 'gif', 'svg'] - -export default Record({ - avg: 0, - url: '', - type: '', - name: '', - chart: [], -}, { - fromJS: pm => { - const type = fileType(pm.url).toLowerCase(); - return { - ...pm, - // type: validTypes.includes(type) ? type : 'n/a', - // fileName: fileName(pm.url) - } - }, -}); \ No newline at end of file diff --git a/frontend/app/types/dashboard/slowestResources.ts b/frontend/app/types/dashboard/slowestResources.ts new file mode 100644 index 000000000..57a802ac6 --- /dev/null +++ b/frontend/app/types/dashboard/slowestResources.ts @@ -0,0 +1,19 @@ +interface ISlowestResources { + avg: number; + url: string; + type: string; + name: string; + chart: any[] +} + +export default class SlowestResources { + avg: ISlowestResources["avg"]; + url: ISlowestResources["url"]; + type: ISlowestResources["type"]; + name: ISlowestResources["name"]; + chart: ISlowestResources["chart"]; + + constructor(data: ISlowestResources) { + Object.assign(this, data); + } +} \ No newline at end of file diff --git a/frontend/app/types/dashboard/speedLocation.js b/frontend/app/types/dashboard/speedLocation.js deleted file mode 100644 index f1774eba0..000000000 --- a/frontend/app/types/dashboard/speedLocation.js +++ /dev/null @@ -1,14 +0,0 @@ -import { Record } from 'immutable'; - -const SpeedLocation = Record({ - avg: undefined, - chart: [], -}); - - -function fromJS(data = {}) { - if (data instanceof SpeedLocation) return data; - return new SpeedLocation(data); -} - -export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/speedLocation.ts b/frontend/app/types/dashboard/speedLocation.ts new file mode 100644 index 000000000..ee9ff7cd3 --- /dev/null +++ b/frontend/app/types/dashboard/speedLocation.ts @@ -0,0 +1,20 @@ +interface ISpeedLocation { + avg?: number + chart?: any[] +} + +class SpeedLocation { + avg?: ISpeedLocation["avg"] + chart?: ISpeedLocation["chart"] + + constructor(data: ISpeedLocation) { + Object.assign(this, data) + } +} + +function fromJS(data = {}) { + if (data instanceof SpeedLocation) return data; + return new SpeedLocation(data); +} + +export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/timeToRender.js b/frontend/app/types/dashboard/timeToRender.js deleted file mode 100644 index e07521498..000000000 --- a/frontend/app/types/dashboard/timeToRender.js +++ /dev/null @@ -1,13 +0,0 @@ -import { Record } from 'immutable'; - -const TimeToRender = Record({ - avg: undefined, - chart: [], -}); - -function fromJS(data = {}) { - if (data instanceof TimeToRender) return data; - return new TimeToRender(data); -} - -export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/timeToRender.ts b/frontend/app/types/dashboard/timeToRender.ts new file mode 100644 index 000000000..d04b40986 --- /dev/null +++ b/frontend/app/types/dashboard/timeToRender.ts @@ -0,0 +1,20 @@ +interface ITimeToRender { + avg?: number + chart?: any[] +} + +class TimeToRender { + avg: ITimeToRender["avg"] + chart: ITimeToRender["chart"] = [] + + constructor(data: ITimeToRender) { + Object.assign(this, data) + } +} + +function fromJS(data = {}) { + if (data instanceof TimeToRender) return data; + return new TimeToRender(data); +} + +export default fromJS; \ No newline at end of file diff --git a/frontend/app/types/dashboard/topDomains.js b/frontend/app/types/dashboard/topDomains.ts similarity index 50% rename from frontend/app/types/dashboard/topDomains.js rename to frontend/app/types/dashboard/topDomains.ts index fa92db397..dfbb2af7e 100644 --- a/frontend/app/types/dashboard/topDomains.js +++ b/frontend/app/types/dashboard/topDomains.ts @@ -1,8 +1,16 @@ import { Record } from 'immutable'; -const TopDomains = Record({ - chart: [] -}); +interface ITopDomains { + chart?: any[] +} + +class TopDomains { + chart: ITopDomains["chart"] = [] + + constructor(data: ITopDomains) { + this.chart = data.chart + } +} function fromJS(data = {}) { if (data instanceof TopDomains) return data; diff --git a/frontend/app/types/dashboard/topMetrics.js b/frontend/app/types/dashboard/topMetrics.js deleted file mode 100644 index 221f65d6c..000000000 --- a/frontend/app/types/dashboard/topMetrics.js +++ /dev/null @@ -1,19 +0,0 @@ -import Record from 'Types/Record'; - -export default Record({ - avgResponseTime: 0, - requestsCount: 0, - avgTimeTilFirstBite: 0, - avgDomCompleteTime: 0 -}, { - // fromJS: aa => ({ - // avgPageLoad: aa.avgDom, - // avgPageLoadProgress: aa.avgDomProgress, - // avgImgLoad: aa.avgLoad, - // avgImgLoadProgress: aa.avgLoadProgress, - // avgReqLoad: aa.avgFirstPixel, - // avgReqLoadProgress: aa.avgFirstPixelProgress, - // ...aa, - // }), -}); - diff --git a/frontend/app/types/dashboard/topMetrics.ts b/frontend/app/types/dashboard/topMetrics.ts new file mode 100644 index 000000000..59d7c89b7 --- /dev/null +++ b/frontend/app/types/dashboard/topMetrics.ts @@ -0,0 +1,17 @@ +interface ITopMetrics { + avgResponseTime: number + requestsCount: number + avgTimeTilFirstBite: number + avgDomCompleteTime: number +} + +export default class TopMetrics { + avgResponseTime: ITopMetrics["avgResponseTime"] = 0 + requestsCount: ITopMetrics["requestsCount"] = 0 + avgTimeTilFirstBite: ITopMetrics["avgTimeTilFirstBite"] = 0 + avgDomCompleteTime: ITopMetrics["avgDomCompleteTime"] = 0 + + constructor(data: ITopMetrics) { + Object.assign(this, data) + } +} \ No newline at end of file diff --git a/frontend/app/types/dashboard/userActivity.js b/frontend/app/types/dashboard/userActivity.js deleted file mode 100644 index 788349337..000000000 --- a/frontend/app/types/dashboard/userActivity.js +++ /dev/null @@ -1,10 +0,0 @@ -import Record from 'Types/Record'; - -export default Record({ - avgVisitedPages: undefined, - avgVisitedPagesProgress: undefined, - avgDuration: undefined, - avgDurationProgress: undefined, - avgEmotionalRating: undefined, - avgEmotionalRatingProgress: undefined, -}); \ No newline at end of file diff --git a/frontend/app/types/dashboard/userActivity.ts b/frontend/app/types/dashboard/userActivity.ts new file mode 100644 index 000000000..835fa4b8a --- /dev/null +++ b/frontend/app/types/dashboard/userActivity.ts @@ -0,0 +1,17 @@ +interface IUserActivity { + avgVisitedPages: number; + avgVisitedPagesProgress: number; + avgDuration: number; + avgDurationProgress: number; +} + +export default class UserActivity { + avgVisitedPages: IUserActivity["avgVisitedPages"] + avgVisitedPagesProgress: IUserActivity["avgDurationProgress"] + avgDuration: IUserActivity["avgDuration"] + avgDurationProgress: IUserActivity["avgDurationProgress"] + + constructor(activity: IUserActivity) { + Object.assign(this, activity) + } +} \ No newline at end of file diff --git a/frontend/app/types/session/session.ts b/frontend/app/types/session/session.ts index d0bf42936..4dcef8d49 100644 --- a/frontend/app/types/session/session.ts +++ b/frontend/app/types/session/session.ts @@ -60,7 +60,7 @@ export interface ISession { eventsCount: number, pagesCount: number, errorsCount: number, - issueTypes: [], + issueTypes: string[], issues: [], referrer: string | null, userDeviceHeapSize: number,