From ccaf869ce9b6036044c01f2bf819e1dfbbfad11a Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 10:50:36 +0200 Subject: [PATCH 01/16] change(ui) - session list icons --- .../shared/AnimatedSVG/AnimatedSVG.tsx | 42 ++++++++++++------- .../components/SessionList/SessionList.tsx | 32 ++++++++++++-- frontend/app/svg/ca-no-bookmarked-session.svg | 14 +++++++ frontend/app/svg/ca-no-live-sessions.svg | 15 +++++++ frontend/app/svg/ca-no-sessions-in-vault.svg | 14 +++++++ frontend/app/svg/ca-no-sessions.svg | 13 ++++++ 6 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 frontend/app/svg/ca-no-bookmarked-session.svg create mode 100644 frontend/app/svg/ca-no-live-sessions.svg create mode 100644 frontend/app/svg/ca-no-sessions-in-vault.svg create mode 100644 frontend/app/svg/ca-no-sessions.svg diff --git a/frontend/app/components/shared/AnimatedSVG/AnimatedSVG.tsx b/frontend/app/components/shared/AnimatedSVG/AnimatedSVG.tsx index 45f4d701d..dc70cdb4c 100644 --- a/frontend/app/components/shared/AnimatedSVG/AnimatedSVG.tsx +++ b/frontend/app/components/shared/AnimatedSVG/AnimatedSVG.tsx @@ -6,6 +6,10 @@ import DashboardSvg from '../../../svg/dashboard-icn.svg'; import LoaderSVG from '../../../svg/openreplay-preloader.svg'; import SignalGreenSvg from '../../../svg/signal-green.svg'; import SignalRedSvg from '../../../svg/signal-red.svg'; +import NoBookmarks from '../../../svg/ca-no-bookmarked-session.svg'; +import NoLiveSessions from '../../../svg/ca-no-live-sessions.svg'; +import NoSessions from '../../../svg/ca-no-sessions.svg'; +import NoSessionsInVault from '../../../svg/ca-no-sessions-in-vault.svg'; export enum ICONS { DASHBOARD_ICON = 'dashboard-icn', @@ -14,7 +18,11 @@ export enum ICONS { NO_RESULTS = 'no-results', LOADER = 'openreplay-preloader', SIGNAL_GREEN = 'signal-green', - SIGNAL_RED = 'signal-red' + SIGNAL_RED = 'signal-red', + NO_BOOKMARKS = 'ca-no-bookmarked-session', + NO_LIVE_SESSIONS = 'ca-no-live-sessions', + NO_SESSIONS = 'ca-no-sessions', + NO_SESSIONS_IN_VAULT = 'ca-no-sessions-in-vault' } interface Props { @@ -26,28 +34,32 @@ function AnimatedSVG(props: Props) { const renderSvg = () => { switch (name) { case ICONS.LOADER: - return + return ; case ICONS.DASHBOARD_ICON: - return + return ; case ICONS.EMPTY_STATE: - return + return ; case ICONS.LOGO_SMALL: - return + return ; case ICONS.NO_RESULTS: - return + return ; case ICONS.SIGNAL_GREEN: - return + return ; case ICONS.SIGNAL_RED: - return + return ; + case ICONS.NO_BOOKMARKS: + return ; + case ICONS.NO_LIVE_SESSIONS: + return ; + case ICONS.NO_SESSIONS: + return ; + case ICONS.NO_SESSIONS_IN_VAULT: + return ; default: return null; } - } - return ( -
- {renderSvg()} -
- ); + }; + return
{renderSvg()}
; } -export default AnimatedSVG; \ No newline at end of file +export default AnimatedSVG; diff --git a/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx b/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx index b5a475073..985fbbc9e 100644 --- a/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx +++ b/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx @@ -20,17 +20,38 @@ interface Props { updateCurrentPage: (page: number) => void; setScrollPosition: (scrollPosition: number) => void; fetchSessions: () => void; + activeTab: any; + isEnterprise?: boolean; } function SessionList(props: Props) { - const { loading, list, currentPage, total, filters, lastPlayedSessionId, metaList } = props; + const { loading, list, currentPage, total, filters, lastPlayedSessionId, metaList, activeTab, isEnterprise = false } = props; const _filterKeys = filters.map((i: any) => i.key); const hasUserFilter = _filterKeys.includes(FilterKey.USERID) || _filterKeys.includes(FilterKey.USERANONYMOUSID); + const isBookmark = activeTab.type === 'bookmark'; + const isVault = isBookmark && isEnterprise; + const NO_CONTENT = React.useMemo(() => { + if (isBookmark && !isEnterprise) { + return { + icon: ICONS.NO_BOOKMARKS, + message: 'You have no bookmarks', + }; + } else if (isVault) { + return { + icon: ICONS.NO_SESSIONS_IN_VAULT, + message: 'You have no sessions in your vault', + }; + } + return { + icon: ICONS.NO_SESSIONS, + message: activeTab.type === 'all' ? 'You have no sessions' : 'You have no sessions with ' + activeTab.name, + }; + }, [isBookmark, isVault, activeTab]); useEffect(() => { const { scrollY } = props; window.scrollTo(0, scrollY); if (total === 0) { - props.fetchSessions() + props.fetchSessions(); } return () => { @@ -51,9 +72,10 @@ function SessionList(props: Props) { - +
- +
{NO_CONTENT.message}
+ {/* */}
} subtext={
Please try changing your search parameters.
} @@ -97,6 +119,8 @@ export default connect( currentPage: state.getIn(['search', 'currentPage']) || 1, total: state.getIn(['sessions', 'total']) || 0, scrollY: state.getIn(['search', 'scrollY']), + activeTab: state.getIn(['search', 'activeTab']), + isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee', }), { updateCurrentPage, addFilterByKeyAndValue, setScrollPosition, fetchSessions } )(SessionList); diff --git a/frontend/app/svg/ca-no-bookmarked-session.svg b/frontend/app/svg/ca-no-bookmarked-session.svg new file mode 100644 index 000000000..f3d84d404 --- /dev/null +++ b/frontend/app/svg/ca-no-bookmarked-session.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/app/svg/ca-no-live-sessions.svg b/frontend/app/svg/ca-no-live-sessions.svg new file mode 100644 index 000000000..979060448 --- /dev/null +++ b/frontend/app/svg/ca-no-live-sessions.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/app/svg/ca-no-sessions-in-vault.svg b/frontend/app/svg/ca-no-sessions-in-vault.svg new file mode 100644 index 000000000..ca9bbc9c7 --- /dev/null +++ b/frontend/app/svg/ca-no-sessions-in-vault.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/app/svg/ca-no-sessions.svg b/frontend/app/svg/ca-no-sessions.svg new file mode 100644 index 000000000..e38cd449a --- /dev/null +++ b/frontend/app/svg/ca-no-sessions.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + From 58166bffa7f7394fd24eb341ebb83b01efda294a Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 11:41:34 +0200 Subject: [PATCH 02/16] change(ui) - force boolean to refresh sessions --- frontend/app/duck/search.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js index 4acb83313..3abeb408c 100644 --- a/frontend/app/duck/search.js +++ b/frontend/app/duck/search.js @@ -150,7 +150,7 @@ export const reduceThenFetchResource = filter.filters = filter.filters.map(filterMap); filter.limit = 10; filter.page = getState().getIn(['search', 'currentPage']); - const forceFetch = filter.filters.length === 0; + const forceFetch = filter.filters.length === 0 || args[1] === true; // duration filter from local storage if (!filter.filters.find((f) => f.type === FilterKey.DURATION)) { @@ -204,10 +204,10 @@ export const remove = (id) => (dispatch, getState) => { // export const remove = createRemove(name, (id) => `/saved_search/${id}`); -export const applyFilter = reduceThenFetchResource((filter, fromUrl = false) => ({ +export const applyFilter = reduceThenFetchResource((filter, force = false) => ({ type: APPLY, filter, - fromUrl, + force, })); export const updateCurrentPage = reduceThenFetchResource((page) => ({ @@ -223,9 +223,9 @@ export const applySavedSearch = (filter) => (dispatch, getState) => { }); }; -export const fetchSessions = (filter) => (dispatch, getState) => { +export const fetchSessions = (filter, force = false) => (dispatch, getState) => { const _filter = filter ? filter : getState().getIn(['search', 'instance']); - return dispatch(applyFilter(_filter)); + return dispatch(applyFilter(_filter, force)); }; export const updateSeries = (index, series) => ({ From 7f7082f127a0f004e258206a5fb31a3c18734708 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 11:41:56 +0200 Subject: [PATCH 03/16] change(ui) - live list no content changes --- .../LiveSessionList/LiveSessionList.tsx | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx index fae2e40cf..8ad601039 100644 --- a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx +++ b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx @@ -1,6 +1,6 @@ import React, { Fragment, useEffect } from 'react'; import { connect } from 'react-redux'; -import { NoContent, Loader, Pagination } from 'UI'; +import { NoContent, Loader, Pagination, Button } from 'UI'; import { List } from 'immutable'; import SessionItem from 'Shared/SessionItem'; import withPermissions from 'HOCs/withPermissions'; @@ -13,6 +13,7 @@ import SortOrderButton from 'Shared/SortOrderButton'; import { capitalize } from 'App/utils'; import LiveSessionReloadButton from 'Shared/LiveSessionReloadButton'; import cn from 'classnames'; +import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; const AUTOREFRESH_INTERVAL = 0.5 * 60 * 1000; const PER_PAGE = 10; @@ -39,11 +40,14 @@ function LiveSessionList(props: Props) { var timeoutId: any; const { filters } = filter; const hasUserFilter = filters.map((i: any) => i.key).includes(KEYS.USERID); - const sortOptions = [{ label: 'Newest', value: 'timestamp' }].concat(metaList - .map((i: any) => ({ - label: capitalize(i), - value: i, - })).toJS()); + const sortOptions = [{ label: 'Newest', value: 'timestamp' }].concat( + metaList + .map((i: any) => ({ + label: capitalize(i), + value: i, + })) + .toJS() + ); useEffect(() => { if (metaListLoading) return; @@ -108,17 +112,28 @@ function LiveSessionList(props: Props) {
- See how to setup the{' '} - - {'Assist'} - {' '} - plugin, if you haven’t done that already. - + title={ +
+ +
+
No live sessions found.
+
} - image={} + subtext={ +
+ + Assist allows you to support your users through live screen viewing and audio/video calls.{' '} + + {'Learn More'} + + + + +
+ } + // image={} show={!loading && list.size === 0} >
@@ -139,7 +154,7 @@ function LiveSessionList(props: Props) { -
+
Date: Thu, 11 Aug 2022 11:42:16 +0200 Subject: [PATCH 04/16] change(ui) - session list no content changes --- .../components/SessionList/SessionList.tsx | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx b/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx index 985fbbc9e..6c043f99a 100644 --- a/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx +++ b/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import { connect } from 'react-redux'; import { FilterKey } from 'Types/filter/filterType'; import SessionItem from 'Shared/SessionItem'; -import { NoContent, Loader, Pagination } from 'UI'; +import { NoContent, Loader, Pagination, Button } from 'UI'; import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; import NoContentMessage from '../NoContentMessage'; import { fetchSessions, addFilterByKeyAndValue, updateCurrentPage, setScrollPosition } from 'Duck/search'; @@ -19,7 +19,7 @@ interface Props { addFilterByKeyAndValue: (key: string, value: any, operator?: string) => void; updateCurrentPage: (page: number) => void; setScrollPosition: (scrollPosition: number) => void; - fetchSessions: () => void; + fetchSessions: (filters: any, force: boolean) => void; activeTab: any; isEnterprise?: boolean; } @@ -33,17 +33,17 @@ function SessionList(props: Props) { if (isBookmark && !isEnterprise) { return { icon: ICONS.NO_BOOKMARKS, - message: 'You have no bookmarks', + message: 'No sessions bookmarked.', }; } else if (isVault) { return { icon: ICONS.NO_SESSIONS_IN_VAULT, - message: 'You have no sessions in your vault', + message: 'No sessions found in vault.', }; } return { icon: ICONS.NO_SESSIONS, - message: activeTab.type === 'all' ? 'You have no sessions' : 'You have no sessions with ' + activeTab.name, + message: 'No relevant sessions found for the selected time period.', }; }, [isBookmark, isVault, activeTab]); @@ -78,7 +78,20 @@ function SessionList(props: Props) { {/* */}
} - subtext={
Please try changing your search parameters.
} + subtext={ +
+ {(isVault || isBookmark) && ( +
+ {isVault + ? 'Add a session to your vault from player screen to retain it for ever.' + : 'Bookmark important sessions in player screen and quickly find them here.'} +
+ )} + +
+ } show={!loading && list.size === 0} > {list.map((session: any) => ( From 40e9b376dcfb9bf70aaba2b5881d5d9dc0f7985c Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 11:42:37 +0200 Subject: [PATCH 05/16] change(ui) - no content css changes --- .../components/ui/NoContent/noContent.module.css | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/frontend/app/components/ui/NoContent/noContent.module.css b/frontend/app/components/ui/NoContent/noContent.module.css index 33c63c916..9b525dc26 100644 --- a/frontend/app/components/ui/NoContent/noContent.module.css +++ b/frontend/app/components/ui/NoContent/noContent.module.css @@ -7,10 +7,10 @@ align-items: center; flex-direction: column; justify-content: center; - color: $gray-medium; - font-weight: 300; + color: $gray-dark; + /* font-weight: 500; */ transition: all 0.2s; - padding-top: 40px; + padding: 40px; &.small { & .title { @@ -24,12 +24,13 @@ } .title { - font-size: 32px; - margin-bottom: 15px; + font-size: 16px; + font-weight: 500; + /* margin-bottom: 15px; */ } .subtext { - font-size: 16px; + font-size: 14px; margin-bottom: 20px; } From bf74420dbf8aafd15a250717091da342acee6ba9 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 11:42:54 +0200 Subject: [PATCH 06/16] change(ui) - google font changes --- frontend/app/assets/index.html | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/frontend/app/assets/index.html b/frontend/app/assets/index.html index b2e0d8dc9..75914f4fb 100644 --- a/frontend/app/assets/index.html +++ b/frontend/app/assets/index.html @@ -1,17 +1,21 @@ - - OpenReplay - - - - - - - - - - -

Loading...

- + + OpenReplay + + + + + + + + + + + + + + +

Loading...

+ From e6a64f673df36cf2e648ec215dea0f49782cfa8b Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 11:43:54 +0200 Subject: [PATCH 07/16] change(ui) - vault no content alignments --- .../SessionListContainer/components/SessionList/SessionList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx b/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx index 6c043f99a..93fd23315 100644 --- a/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx +++ b/frontend/app/components/shared/SessionListContainer/components/SessionList/SessionList.tsx @@ -79,7 +79,7 @@ function SessionList(props: Props) {
} subtext={ -
+
{(isVault || isBookmark) && (
{isVault From be368c2a8a94b7b0fb2a403da6f9b524823a86db Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 11:59:56 +0200 Subject: [PATCH 08/16] change(ui) - session search filed placeholder text --- .../SessionSearchField/SessionSearchField.tsx | 101 +++++++++--------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx b/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx index 9796c441c..4f3c3d121 100644 --- a/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx +++ b/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx @@ -3,64 +3,67 @@ import { connect } from 'react-redux'; import { Input } from 'UI'; import FilterModal from 'Shared/Filters/FilterModal'; import { debounce } from 'App/utils'; -import { assist as assistRoute, isRoute } from "App/routes"; +import { assist as assistRoute, isRoute } from 'App/routes'; const ASSIST_ROUTE = assistRoute(); interface Props { - fetchFilterSearch: (query: any) => void; - addFilterByKeyAndValue: (key: string, value: string) => void; - filterList: any; - filterListLive: any; - filterSearchListLive: any; - filterSearchList: any; + fetchFilterSearch: (query: any) => void; + addFilterByKeyAndValue: (key: string, value: string) => void; + filterList: any; + filterListLive: any; + filterSearchListLive: any; + filterSearchList: any; } function SessionSearchField(props: Props) { - const debounceFetchFilterSearch = React.useCallback(debounce(props.fetchFilterSearch, 1000), []); - const [showModal, setShowModal] = useState(false) - const [searchQuery, setSearchQuery] = useState('') + const debounceFetchFilterSearch = React.useCallback(debounce(props.fetchFilterSearch, 1000), []); + const [showModal, setShowModal] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); - const onSearchChange = ({ target: { value } }: any) => { - setSearchQuery(value) - debounceFetchFilterSearch({ q: value }); - } + const onSearchChange = ({ target: { value } }: any) => { + setSearchQuery(value); + debounceFetchFilterSearch({ q: value }); + }; - const onAddFilter = (filter: any) => { - props.addFilterByKeyAndValue(filter.key, filter.value) - } + const onAddFilter = (filter: any) => { + props.addFilterByKeyAndValue(filter.key, filter.value); + }; - return ( -
- setShowModal(true) } - onBlur={ () => setTimeout(setShowModal, 200, false) } - onChange={ onSearchChange } - placeholder={ 'Search sessions using any captured event (click, input, page, error...)'} - id="search" - type="search" - autoComplete="off" - className="hover:border-gray-medium" - /> + return ( +
+ setShowModal(true)} + onBlur={() => setTimeout(setShowModal, 200, false)} + onChange={onSearchChange} + placeholder={'Search sessions using any captured event (click, input, page, error...)'} + id="search" + type="search" + autoComplete="off" + className="hover:border-gray-medium text-lg placeholder-lg" + /> - { showModal && ( -
- + {showModal && ( +
+ +
+ )}
- )} -
- ); + ); } -export default connect((state: any) => ({ - filterSearchList: state.getIn([ 'search', 'filterSearchList' ]), - filterSearchListLive: state.getIn([ 'liveSearch', 'filterSearchList' ]), - filterList: state.getIn([ 'search', 'filterList' ]), - filterListLive: state.getIn([ 'search', 'filterListLive' ]), -}), { })(SessionSearchField); +export default connect( + (state: any) => ({ + filterSearchList: state.getIn(['search', 'filterSearchList']), + filterSearchListLive: state.getIn(['liveSearch', 'filterSearchList']), + filterList: state.getIn(['search', 'filterList']), + filterListLive: state.getIn(['search', 'filterListLive']), + }), + {} +)(SessionSearchField); From c6ec3cc7de302b8398e4579437a29cdb0d13a0ba Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 12:00:14 +0200 Subject: [PATCH 09/16] change(ui) - no records yet message changes --- .../NoSessionsMessage/NoSessionsMessage.js | 85 +++++++++++-------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js b/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js index 01f8a66b2..79dc8e436 100644 --- a/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js +++ b/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js @@ -1,40 +1,55 @@ -import React from 'react' -import { Icon, Button } from 'UI' -import { connect } from 'react-redux' -import { onboarding as onboardingRoute } from 'App/routes' +import React from 'react'; +import { Icon, Button } from 'UI'; +import { connect } from 'react-redux'; +import { onboarding as onboardingRoute } from 'App/routes'; import { withRouter } from 'react-router-dom'; import * as routes from '../../../routes'; const withSiteId = routes.withSiteId; -const NoSessionsMessage= (props) => { - const { sites, match: { params: { siteId } } } = props; - const activeSite = sites.find(s => s.id == siteId); - const showNoSessions = !!activeSite && !activeSite.recorded; - return ( - <> - {showNoSessions && ( -
-
-
-
- -
-
- It takes a few minutes for first recordings to appear. All set but they are still not showing up? Check our troubleshooting section. -
- -
-
-
- )} - - ) -} +const NoSessionsMessage = (props) => { + const { + sites, + match: { + params: { siteId }, + }, + } = props; + const activeSite = sites.find((s) => s.id == siteId); + const showNoSessions = !!activeSite && !activeSite.recorded; + return ( + <> + {showNoSessions && ( +
+
+
+
+ +
+
+ It might take a few minutes for first recording to appear. + + Troubleshoot + + . +
+ +
+
+
+ )} + + ); +}; -export default connect(state => ({ - site: state.getIn([ 'site', 'siteId' ]), - sites: state.getIn([ 'site', 'list' ]) -}))(withRouter(NoSessionsMessage)) \ No newline at end of file +export default connect((state) => ({ + site: state.getIn(['site', 'siteId']), + sites: state.getIn(['site', 'list']), +}))(withRouter(NoSessionsMessage)); From be91fa3fb8ea76c62957057488d50993f6940c38 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 12:01:07 +0200 Subject: [PATCH 10/16] change(ui) - session search filed placeholder text --- frontend/app/styles/general.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/app/styles/general.css b/frontend/app/styles/general.css index 05e1d31f3..f0b80a54d 100644 --- a/frontend/app/styles/general.css +++ b/frontend/app/styles/general.css @@ -143,6 +143,11 @@ font-size: 14px; } +.placeholder-lg::placeholder { + color: $gray-medium !important; + font-size: 16px; +} + .ui[class*="top fixed"].menu { background-color: white !important; border-bottom: solid thin #ddd !important; From c1bbeb61c031d3e5f1850f121ee345a10f1df228 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 12:01:36 +0200 Subject: [PATCH 11/16] change(ui) - live list pagination --- .../LiveSessionList/LiveSessionList.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx index 8ad601039..b6e64c57c 100644 --- a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx +++ b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx @@ -151,18 +151,18 @@ function LiveSessionList(props: Props) { ))}
+ +
+ props.updateCurrentPage(page)} + limit={PER_PAGE} + debounceRequest={500} + /> +
- -
- props.updateCurrentPage(page)} - limit={PER_PAGE} - debounceRequest={500} - /> -
); From 28e1492eb818c088e00cb16616a57955d9d7add0 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 12:10:07 +0200 Subject: [PATCH 12/16] change(ui) - alert no content message and other changes --- frontend/app/components/Alerts/AlertForm.js | 601 +++++++++--------- .../Alerts/AlertFormModal/AlertFormModal.tsx | 167 ++--- frontend/app/components/Alerts/Alerts.js | 173 ++--- frontend/app/components/Alerts/AlertsList.js | 99 +-- .../ui/NoContent/noContent.module.css | 4 +- frontend/app/types/alert.js | 2 +- 6 files changed, 524 insertions(+), 522 deletions(-) diff --git a/frontend/app/components/Alerts/AlertForm.js b/frontend/app/components/Alerts/AlertForm.js index 1701c8e0a..f5e4ee236 100644 --- a/frontend/app/components/Alerts/AlertForm.js +++ b/frontend/app/components/Alerts/AlertForm.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react' +import React, { useEffect } from 'react'; import { Button, Form, Input, SegmentSelection, Checkbox, Message, Link, Icon } from 'UI'; import { alertMetrics as metrics } from 'App/constants'; import { alertConditions as conditions } from 'App/constants'; @@ -9,333 +9,322 @@ import DropdownChips from './DropdownChips'; import { validateEmail } from 'App/validate'; import cn from 'classnames'; import { fetchTriggerOptions } from 'Duck/alerts'; -import Select from 'Shared/Select' +import Select from 'Shared/Select'; const thresholdOptions = [ - { label: '15 minutes', value: 15 }, - { label: '30 minutes', value: 30 }, - { label: '1 hour', value: 60 }, - { label: '2 hours', value: 120 }, - { label: '4 hours', value: 240 }, - { label: '1 day', value: 1440 }, + { label: '15 minutes', value: 15 }, + { label: '30 minutes', value: 30 }, + { label: '1 hour', value: 60 }, + { label: '2 hours', value: 120 }, + { label: '4 hours', value: 240 }, + { label: '1 day', value: 1440 }, ]; const changeOptions = [ - { label: 'change', value: 'change' }, - { label: '% change', value: 'percent' }, + { label: 'change', value: 'change' }, + { label: '% change', value: 'percent' }, ]; -const Circle = ({ text }) => ( -
{text}
-) +const Circle = ({ text }) =>
{text}
; const Section = ({ index, title, description, content }) => ( -
-
- -
- {title} - { description &&
{description}
} -
-
+
+
+ +
+ {title} + {description &&
{description}
} +
+
-
- {content} +
{content}
-
-) +); const integrationsRoute = client(CLIENT_TABS.INTEGRATIONS); -const AlertForm = props => { - const { instance, slackChannels, webhooks, loading, onDelete, deleting, triggerOptions, metricId, style={ width: '580px', height: '100vh' } } = props; - const write = ({ target: { value, name } }) => props.edit({ [ name ]: value }) - const writeOption = (e, { name, value }) => props.edit({ [ name ]: value.value }); - const onChangeCheck = ({ target: { checked, name }}) => props.edit({ [ name ]: checked }) - // const onChangeOption = ({ checked, name }) => props.edit({ [ name ]: checked }) - // const onChangeCheck = (e) => { console.log(e) } +const AlertForm = (props) => { + const { + instance, + slackChannels, + webhooks, + loading, + onDelete, + deleting, + triggerOptions, + metricId, + style = { width: '580px', height: '100vh' }, + } = props; + const write = ({ target: { value, name } }) => props.edit({ [name]: value }); + const writeOption = (e, { name, value }) => props.edit({ [name]: value.value }); + const onChangeCheck = ({ target: { checked, name } }) => props.edit({ [name]: checked }); + // const onChangeOption = ({ checked, name }) => props.edit({ [ name ]: checked }) + // const onChangeCheck = (e) => { console.log(e) } - useEffect(() => { - props.fetchTriggerOptions(); - }, []) + useEffect(() => { + props.fetchTriggerOptions(); + }, []); - const writeQueryOption = (e, { name, value }) => { - const { query } = instance; - props.edit({ query: { ...query, [name] : value } }); - } + const writeQueryOption = (e, { name, value }) => { + const { query } = instance; + props.edit({ query: { ...query, [name]: value } }); + }; - const writeQuery = ({ target: { value, name } }) => { - const { query } = instance; - props.edit({ query: { ...query, [name] : value } }); - } + const writeQuery = ({ target: { value, name } }) => { + const { query } = instance; + props.edit({ query: { ...query, [name]: value } }); + }; - const metric = (instance && instance.query.left) ? triggerOptions.find(i => i.value === instance.query.left) : null; - const unit = metric ? metric.unit : ''; - const isThreshold = instance.detectionMethod === 'threshold'; + const metric = instance && instance.query.left ? triggerOptions.find((i) => i.value === instance.query.left) : null; + const unit = metric ? metric.unit : ''; + const isThreshold = instance.detectionMethod === 'threshold'; - return ( -
props.onSubmit(instance)} id="alert-form"> -
- -
-
- props.edit({ [ name ]: value }) } - value={{ value: instance.detectionMethod }} - list={ [ - { name: 'Threshold', value: 'threshold' }, - { name: 'Change', value: 'change' }, - ]} - /> -
- {isThreshold && 'Eg. Alert me if memory.avg is greater than 500mb over the past 4 hours.'} - {!isThreshold && 'Eg. Alert me if % change of memory.avg is greater than 10% over the past 4 hours compared to the previous 4 hours.'} -
-
+ return ( + props.onSubmit(instance)} id="alert-form"> +
+ +
+
+ props.edit({ [name]: value })} + value={{ value: instance.detectionMethod }} + list={[ + { name: 'Threshold', value: 'threshold' }, + { name: 'Change', value: 'change' }, + ]} + /> +
+ {isThreshold && 'Eg. Alert me if memory.avg is greater than 500mb over the past 4 hours.'} + {!isThreshold && + 'Eg. Alert me if % change of memory.avg is greater than 10% over the past 4 hours compared to the previous 4 hours.'} +
+
+
+ } + /> + +
+ +
+ {!isThreshold && ( +
+ + i.value === instance.query.left)} + // onChange={ writeQueryOption } + onChange={({ value }) => writeQueryOption(null, { name: 'left', value: value.value })} + /> +
+ +
+ +
+ + {'test'} + + )} + {!unit && ( + + )} +
+
+ +
+ + writeOption(null, { name: 'previousPeriod', value })} + /> +
+ )} +
+ } + /> + +
+ +
+
+ + + +
+ + {instance.slack && ( +
+ +
+ props.edit({ slackInput: selected })} + /> +
+
+ )} + + {instance.email && ( +
+ +
+ props.edit({ emailInput: selected })} + /> +
+
+ )} + + {instance.webhook && ( +
+ + props.edit({ webhookInput: selected })} + /> +
+ )} +
+ } + />
- } - /> -
- -
- {!isThreshold && ( -
- - i.value === instance.query.left) } - // onChange={ writeQueryOption } - onChange={ ({ value }) => writeQueryOption(null, { name: 'left', value: value.value }) } - /> -
- -
- -
- - {'test'} - - )} - { !unit && ( - - )} +
+ {instance.exists() && ( + + )}
-
- -
- - writeOption(null, { name: 'previousPeriod', value }) } - /> -
- )}
- } - /> + + ); +}; -
- -
-
- - - -
- - { instance.slack && ( -
- -
- props.edit({ 'slackInput': selected })} - /> -
-
- )} - - {instance.email && ( -
- -
- props.edit({ 'emailInput': selected })} - /> -
-
- )} - - - {instance.webhook && ( -
- - props.edit({ 'webhookInput': selected })} - /> -
- )} -
- } - /> -
- - -
-
- -
- -
-
- {instance.exists() && ( - - )} -
-
- - ) -} - -export default connect(state => ({ - instance: state.getIn(['alerts', 'instance']), - triggerOptions: state.getIn(['alerts', 'triggerOptions']), - loading: state.getIn(['alerts', 'saveRequest', 'loading']), - deleting: state.getIn(['alerts', 'removeRequest', 'loading']) -}), { fetchTriggerOptions })(AlertForm) +export default connect( + (state) => ({ + instance: state.getIn(['alerts', 'instance']), + triggerOptions: state.getIn(['alerts', 'triggerOptions']), + loading: state.getIn(['alerts', 'saveRequest', 'loading']), + deleting: state.getIn(['alerts', 'removeRequest', 'loading']), + }), + { fetchTriggerOptions } +)(AlertForm); diff --git a/frontend/app/components/Alerts/AlertFormModal/AlertFormModal.tsx b/frontend/app/components/Alerts/AlertFormModal/AlertFormModal.tsx index 8869f3a02..dc4c9db15 100644 --- a/frontend/app/components/Alerts/AlertFormModal/AlertFormModal.tsx +++ b/frontend/app/components/Alerts/AlertFormModal/AlertFormModal.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState } from 'react'; import { SlideModal, IconButton } from 'UI'; import { init, edit, save, remove } from 'Duck/alerts'; import { fetchList as fetchWebhooks } from 'Duck/webhook'; @@ -9,93 +9,98 @@ import { EMAIL, SLACK, WEBHOOK } from 'App/constants/schedule'; import { confirm } from 'UI'; interface Props { - showModal?: boolean; - metricId?: number; - onClose?: () => void; - webhooks: any; - fetchWebhooks: Function; - save: Function; - remove: Function; - init: Function; - edit: Function; + showModal?: boolean; + metricId?: number; + onClose?: () => void; + webhooks: any; + fetchWebhooks: Function; + save: Function; + remove: Function; + init: Function; + edit: Function; } function AlertFormModal(props: Props) { - const { metricId = null, showModal = false, webhooks } = props; - const [showForm, setShowForm] = useState(false); + const { metricId = null, showModal = false, webhooks } = props; + const [showForm, setShowForm] = useState(false); - useEffect(() => { - props.fetchWebhooks(); - }, []) + useEffect(() => { + props.fetchWebhooks(); + }, []); - const slackChannels = webhooks.filter(hook => hook.type === SLACK).map(({ webhookId, name }) => ({ value: webhookId, text: name })).toJS(); - const hooks = webhooks.filter(hook => hook.type === WEBHOOK).map(({ webhookId, name }) => ({ value: webhookId, text: name })).toJS(); + const slackChannels = webhooks + .filter((hook) => hook.type === SLACK) + .map(({ webhookId, name }) => ({ value: webhookId, text: name })) + .toJS(); + const hooks = webhooks + .filter((hook) => hook.type === WEBHOOK) + .map(({ webhookId, name }) => ({ value: webhookId, text: name })) + .toJS(); - const saveAlert = instance => { - const wasUpdating = instance.exists(); - props.save(instance).then(() => { - if (!wasUpdating) { - toggleForm(null, false); - } - if (props.onClose) { - props.onClose(); - } - }) - } + const saveAlert = (instance) => { + const wasUpdating = instance.exists(); + props.save(instance).then(() => { + if (!wasUpdating) { + toggleForm(null, false); + } + if (props.onClose) { + props.onClose(); + } + }); + }; - const onDelete = async (instance) => { - if (await confirm({ - header: 'Confirm', - confirmButton: 'Yes, delete', - confirmation: `Are you sure you want to permanently delete this alert?` - })) { - props.remove(instance.alertId).then(() => { - toggleForm(null, false); - }); - } - } - - const toggleForm = (instance, state) => { - if (instance) { - props.init(instance) - } - return setShowForm(state ? state : !showForm); - } - - return ( - - { 'Create Alert' } - {/* toggleForm({}, true) } - /> */} -
+ const onDelete = async (instance) => { + if ( + await confirm({ + header: 'Confirm', + confirmButton: 'Yes, delete', + confirmation: `Are you sure you want to permanently delete this alert?`, + }) + ) { + props.remove(instance.alertId).then(() => { + toggleForm(null, false); + }); } - isDisplayed={ showModal } - onClose={props.onClose} - size="medium" - content={ showModal && - { + if (instance) { + props.init(instance); + } + return setShowForm(state ? state : !showForm); + }; + + return ( + + {'Create Alert'} +
+ } + isDisplayed={showModal} onClose={props.onClose} - onDelete={onDelete} - style={{ width: '580px', height: '100vh - 200px' }} - /> - } - /> - ); + size="medium" + content={ + showModal && ( + + ) + } + /> + ); } -export default connect(state => ({ - webhooks: state.getIn(['webhooks', 'list']), - instance: state.getIn(['alerts', 'instance']), -}), { init, edit, save, remove, fetchWebhooks, setShowAlerts })(AlertFormModal) \ No newline at end of file +export default connect( + (state) => ({ + webhooks: state.getIn(['webhooks', 'list']), + instance: state.getIn(['alerts', 'instance']), + }), + { init, edit, save, remove, fetchWebhooks, setShowAlerts } +)(AlertFormModal); diff --git a/frontend/app/components/Alerts/Alerts.js b/frontend/app/components/Alerts/Alerts.js index b24665a68..ed825abaf 100644 --- a/frontend/app/components/Alerts/Alerts.js +++ b/frontend/app/components/Alerts/Alerts.js @@ -10,95 +10,100 @@ import { setShowAlerts } from 'Duck/dashboard'; import { EMAIL, SLACK, WEBHOOK } from 'App/constants/schedule'; import { confirm } from 'UI'; -const Alerts = props => { - const { webhooks, setShowAlerts } = props; - const [showForm, setShowForm] = useState(false); +const Alerts = (props) => { + const { webhooks, setShowAlerts } = props; + const [showForm, setShowForm] = useState(false); - useEffect(() => { - props.fetchWebhooks(); - }, []) + useEffect(() => { + props.fetchWebhooks(); + }, []); - const slackChannels = webhooks.filter(hook => hook.type === SLACK).map(({ webhookId, name }) => ({ value: webhookId, label: name })).toJS(); - const hooks = webhooks.filter(hook => hook.type === WEBHOOK).map(({ webhookId, name }) => ({ value: webhookId, label: name })).toJS(); + const slackChannels = webhooks + .filter((hook) => hook.type === SLACK) + .map(({ webhookId, name }) => ({ value: webhookId, label: name })) + .toJS(); + const hooks = webhooks + .filter((hook) => hook.type === WEBHOOK) + .map(({ webhookId, name }) => ({ value: webhookId, label: name })) + .toJS(); - const saveAlert = instance => { - const wasUpdating = instance.exists(); - props.save(instance).then(() => { - if (!wasUpdating) { - toast.success('New alert saved') - toggleForm(null, false); - } else { - toast.success('Alert updated') - } - }) - } + const saveAlert = (instance) => { + const wasUpdating = instance.exists(); + props.save(instance).then(() => { + if (!wasUpdating) { + toast.success('New alert saved'); + toggleForm(null, false); + } else { + toast.success('Alert updated'); + } + }); + }; - const onDelete = async (instance) => { - if (await confirm({ - header: 'Confirm', - confirmButton: 'Yes, delete', - confirmation: `Are you sure you want to permanently delete this alert?` - })) { - props.remove(instance.alertId).then(() => { - toggleForm(null, false); - }); - } - } + const onDelete = async (instance) => { + if ( + await confirm({ + header: 'Confirm', + confirmButton: 'Yes, delete', + confirmation: `Are you sure you want to permanently delete this alert?`, + }) + ) { + props.remove(instance.alertId).then(() => { + toggleForm(null, false); + }); + } + }; - const toggleForm = (instance, state) => { - if (instance) { - props.init(instance) - } - return setShowForm(state ? state : !showForm); - } + const toggleForm = (instance, state) => { + if (instance) { + props.init(instance); + } + return setShowForm(state ? state : !showForm); + }; - return ( -
- - { 'Alerts' } - toggleForm({}, true) } + return ( +
+ + {'Alerts'} + toggleForm({}, true)} /> +
+ } + isDisplayed={true} + onClose={() => { + toggleForm({}, false); + setShowAlerts(false); + }} + size="small" + content={ + { + toggleForm(alert, true); + }} + onClickCreate={() => toggleForm({}, true)} + /> + } + detailContent={ + showForm && ( + toggleForm({}, false)} + onDelete={onDelete} + /> + ) + } /> -
- } - isDisplayed={ true } - onClose={ () => { - toggleForm({}, false); - setShowAlerts(false); - } } - size="small" - content={ - { - toggleForm(alert, true) - }} - /> - } - detailContent={ - showForm && ( - toggleForm({}, false) } - onDelete={onDelete} - /> - ) - } - /> -
- ) -} +
+ ); +}; -export default connect(state => ({ - webhooks: state.getIn(['webhooks', 'list']), - instance: state.getIn(['alerts', 'instance']), -}), { init, edit, save, remove, fetchWebhooks, setShowAlerts })(Alerts) +export default connect( + (state) => ({ + webhooks: state.getIn(['webhooks', 'list']), + instance: state.getIn(['alerts', 'instance']), + }), + { init, edit, save, remove, fetchWebhooks, setShowAlerts } +)(Alerts); diff --git a/frontend/app/components/Alerts/AlertsList.js b/frontend/app/components/Alerts/AlertsList.js index 21ea6448d..5a874e0fa 100644 --- a/frontend/app/components/Alerts/AlertsList.js +++ b/frontend/app/components/Alerts/AlertsList.js @@ -1,55 +1,58 @@ -import React, { useEffect, useState } from 'react' -import { Loader, NoContent, Input } from 'UI'; -import AlertItem from './AlertItem' +import React, { useEffect, useState } from 'react'; +import { Loader, NoContent, Input, Button } from 'UI'; +import AlertItem from './AlertItem'; import { fetchList, init } from 'Duck/alerts'; import { connect } from 'react-redux'; import { getRE } from 'App/utils'; -const AlertsList = props => { - const { loading, list, instance, onEdit } = props; - const [query, setQuery] = useState('') - - useEffect(() => { - props.fetchList() - }, []) +const AlertsList = (props) => { + const { loading, list, instance, onEdit } = props; + const [query, setQuery] = useState(''); - const filterRE = getRE(query, 'i'); - const _filteredList = list.filter(({ name, query: { left } }) => filterRE.test(name) || filterRE.test(left)); + useEffect(() => { + props.fetchList(); + }, []); - return ( -
-
- setQuery(value)} - /> -
- - -
- {_filteredList.map(a => ( -
- onEdit(a.toData())} - /> -
- ))} -
-
-
-
- ) -} + const filterRE = getRE(query, 'i'); + const _filteredList = list.filter(({ name, query: { left } }) => filterRE.test(name) || filterRE.test(left)); -export default connect(state => ({ - list: state.getIn(['alerts', 'list']).sort((a, b ) => b.createdAt - a.createdAt), - instance: state.getIn(['alerts', 'instance']), - loading: state.getIn(['alerts', 'loading']) -}), { fetchList, init })(AlertsList) + return ( +
+
+ setQuery(value)} /> +
+ + +
Alerts helps your team stay up to date with the activity on your app.
+ +
+ } + size="small" + show={list.size === 0} + > +
+ {_filteredList.map((a) => ( +
+ onEdit(a.toData())} /> +
+ ))} +
+ + +
+ ); +}; + +export default connect( + (state) => ({ + list: state.getIn(['alerts', 'list']).sort((a, b) => b.createdAt - a.createdAt), + instance: state.getIn(['alerts', 'instance']), + loading: state.getIn(['alerts', 'loading']), + }), + { fetchList, init } +)(AlertsList); diff --git a/frontend/app/components/ui/NoContent/noContent.module.css b/frontend/app/components/ui/NoContent/noContent.module.css index 9b525dc26..dd78c8bbc 100644 --- a/frontend/app/components/ui/NoContent/noContent.module.css +++ b/frontend/app/components/ui/NoContent/noContent.module.css @@ -12,7 +12,7 @@ transition: all 0.2s; padding: 40px; - &.small { + /* &.small { & .title { font-size: 20px !important; } @@ -20,7 +20,7 @@ & .subtext { font-size: 16px; } - } + } */ } .title { diff --git a/frontend/app/types/alert.js b/frontend/app/types/alert.js index c16f6a87e..244047a45 100644 --- a/frontend/app/types/alert.js +++ b/frontend/app/types/alert.js @@ -12,7 +12,7 @@ conditions.forEach(c => { conditionsMap[c.value] = c }); export default Record({ alertId: '', projectId: undefined, - name: 'New Alert', + name: 'Untitled Alert', description: '', active: true, currentPeriod: 15, From a66a1341c4f02abcf570fa357cb6cc54df11c549 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 13:46:24 +0200 Subject: [PATCH 13/16] change(ui) - no data message for network tab --- .../Session_/Network/NetworkContent.js | 473 +++++++++--------- 1 file changed, 232 insertions(+), 241 deletions(-) diff --git a/frontend/app/components/Session_/Network/NetworkContent.js b/frontend/app/components/Session_/Network/NetworkContent.js index 8e0183324..b6c54b5b4 100644 --- a/frontend/app/components/Session_/Network/NetworkContent.js +++ b/frontend/app/components/Session_/Network/NetworkContent.js @@ -1,7 +1,7 @@ import React from 'react'; import cn from 'classnames'; // import { connectPlayer } from 'Player'; -import { QuestionMarkHint, Popup, Tabs, Input } from 'UI'; +import { QuestionMarkHint, Popup, Tabs, Input, NoContent, Icon } from 'UI'; import { getRE } from 'App/utils'; import { TYPES } from 'Types/session/resource'; import { formatBytes } from 'App/utils'; @@ -21,270 +21,261 @@ const MEDIA = 'media'; const OTHER = 'other'; const TAB_TO_TYPE_MAP = { - [ XHR ]: TYPES.XHR, - [ JS ]: TYPES.JS, - [ CSS ]: TYPES.CSS, - [ IMG ]: TYPES.IMG, - [ MEDIA ]: TYPES.MEDIA, - [ OTHER ]: TYPES.OTHER -} -const TABS = [ ALL, XHR, JS, CSS, IMG, MEDIA, OTHER ].map(tab => ({ - text: tab, - key: tab, + [XHR]: TYPES.XHR, + [JS]: TYPES.JS, + [CSS]: TYPES.CSS, + [IMG]: TYPES.IMG, + [MEDIA]: TYPES.MEDIA, + [OTHER]: TYPES.OTHER, +}; +const TABS = [ALL, XHR, JS, CSS, IMG, MEDIA, OTHER].map((tab) => ({ + text: tab, + key: tab, })); -const DOM_LOADED_TIME_COLOR = "teal"; -const LOAD_TIME_COLOR = "red"; +const DOM_LOADED_TIME_COLOR = 'teal'; +const LOAD_TIME_COLOR = 'red'; -export function renderType(r) { - return ( - { r.type }
} > -
{ r.type }
- - ); +export function renderType(r) { + return ( + {r.type}}> +
{r.type}
+
+ ); } -export function renderName(r) { - return ( - { r.url } } > -
{ r.name }
-
- ); +export function renderName(r) { + return ( + {r.url}}> +
{r.name}
+
+ ); } const renderXHRText = () => ( - - {XHR} - - Use our Fetch plugin - {' to capture HTTP requests and responses, including status codes and bodies.'}
- We also provide support for GraphQL - {' for easy debugging of your queries.'} - - } - className="ml-1" - /> -
+ + {XHR} + + Use our{' '} + + Fetch plugin + + {' to capture HTTP requests and responses, including status codes and bodies.'}
+ We also provide{' '} + + support for GraphQL + + {' for easy debugging of your queries.'} + + } + className="ml-1" + /> +
); function renderSize(r) { - if (r.responseBodySize) return formatBytes(r.responseBodySize); - let triggerText; - let content; - if (r.decodedBodySize == null) { - triggerText = "x"; - content = "Not captured"; - } else { - const headerSize = r.headerSize || 0; - const encodedSize = r.encodedBodySize || 0; - const transferred = headerSize + encodedSize; - const showTransferred = r.headerSize != null; + if (r.responseBodySize) return formatBytes(r.responseBodySize); + let triggerText; + let content; + if (r.decodedBodySize == null) { + triggerText = 'x'; + content = 'Not captured'; + } else { + const headerSize = r.headerSize || 0; + const encodedSize = r.encodedBodySize || 0; + const transferred = headerSize + encodedSize; + const showTransferred = r.headerSize != null; - triggerText = formatBytes(r.decodedBodySize); - content = ( -
    - { showTransferred && -
  • {`${formatBytes( r.encodedBodySize + headerSize )} transfered over network`}
  • - } -
  • {`Resource size: ${formatBytes(r.decodedBodySize)} `}
  • -
+ triggerText = formatBytes(r.decodedBodySize); + content = ( +
    + {showTransferred &&
  • {`${formatBytes(r.encodedBodySize + headerSize)} transfered over network`}
  • } +
  • {`Resource size: ${formatBytes(r.decodedBodySize)} `}
  • +
+ ); + } + + return ( + +
{triggerText}
+
); - } - - return ( - -
{ triggerText }
-
- ); } export function renderDuration(r) { - if (!r.success) return 'x'; + if (!r.success) return 'x'; - const text = `${ Math.floor(r.duration) }ms`; - if (!r.isRed() && !r.isYellow()) return text; + const text = `${Math.floor(r.duration)}ms`; + if (!r.isRed() && !r.isYellow()) return text; - let tooltipText; - let className = "w-full h-full flex items-center "; - if (r.isYellow()) { - tooltipText = "Slower than average"; - className += "warn color-orange"; - } else { - tooltipText = "Much slower than average"; - className += "error color-red"; - } + let tooltipText; + let className = 'w-full h-full flex items-center '; + if (r.isYellow()) { + tooltipText = 'Slower than average'; + className += 'warn color-orange'; + } else { + tooltipText = 'Much slower than average'; + className += 'error color-red'; + } - return ( - -
{ text }
-
- ); + return ( + +
{text}
+
+ ); } export default class NetworkContent extends React.PureComponent { - state = { - filter: '', - activeTab: ALL, - } + state = { + filter: '', + activeTab: ALL, + }; - onTabClick = activeTab => this.setState({ activeTab }) - onFilterChange = ({ target: { value } }) => this.setState({ filter: value }) + onTabClick = (activeTab) => this.setState({ activeTab }); + onFilterChange = ({ target: { value } }) => this.setState({ filter: value }); - render() { - const { - location, - resources, - domContentLoadedTime, - loadTime, - domBuildingTime, - fetchPresented, - onRowClick, - isResult = false, - additionalHeight = 0, - resourcesSize, - transferredSize, - time, - currentIndex - } = this.props; - const { filter, activeTab } = this.state; - const filterRE = getRE(filter, 'i'); - let filtered = resources.filter(({ type, name }) => - filterRE.test(name) && (activeTab === ALL || type === TAB_TO_TYPE_MAP[ activeTab ])); - const lastIndex = currentIndex || filtered.filter(item => item.time <= time).length - 1; + render() { + const { + location, + resources, + domContentLoadedTime, + loadTime, + domBuildingTime, + fetchPresented, + onRowClick, + isResult = false, + additionalHeight = 0, + resourcesSize, + transferredSize, + time, + currentIndex, + } = this.props; + const { filter, activeTab } = this.state; + const filterRE = getRE(filter, 'i'); + let filtered = resources.filter(({ type, name }) => filterRE.test(name) && (activeTab === ALL || type === TAB_TO_TYPE_MAP[activeTab])); + const lastIndex = currentIndex || filtered.filter((item) => item.time <= time).length - 1; - const referenceLines = []; - if (domContentLoadedTime != null) { - referenceLines.push({ - time: domContentLoadedTime.time, - color: DOM_LOADED_TIME_COLOR, - }) + const referenceLines = []; + if (domContentLoadedTime != null) { + referenceLines.push({ + time: domContentLoadedTime.time, + color: DOM_LOADED_TIME_COLOR, + }); + } + if (loadTime != null) { + referenceLines.push({ + time: loadTime.time, + color: LOAD_TIME_COLOR, + }); + } + + let tabs = TABS; + if (!fetchPresented) { + tabs = TABS.map((tab) => + !isResult && tab.key === XHR + ? { + text: renderXHRText(), + key: XHR, + } + : tab + ); + } + + // const resourcesSize = filtered.reduce((sum, { decodedBodySize }) => sum + (decodedBodySize || 0), 0); + // const transferredSize = filtered + // .reduce((sum, { headerSize, encodedBodySize }) => sum + (headerSize || 0) + (encodedBodySize || 0), 0); + + return ( + + + +
+ Network + +
+ +
+ + + + 0} /> + 0} /> + + + + + + + No Data + + } + size="small" + show={filtered.length === 0} + > + + {[ + { + label: 'Status', + dataKey: 'status', + width: 70, + }, + { + label: 'Type', + dataKey: 'type', + width: 90, + render: renderType, + }, + { + label: 'Name', + width: 200, + render: renderName, + }, + { + label: 'Size', + width: 60, + render: renderSize, + }, + { + label: 'Time', + width: 80, + render: renderDuration, + }, + ]} + + + +
+
+ ); } - if (loadTime != null) { - referenceLines.push({ - time: loadTime.time, - color: LOAD_TIME_COLOR, - }) - } - - let tabs = TABS; - if (!fetchPresented) { - tabs = TABS.map(tab => !isResult && tab.key === XHR - ? { - text: renderXHRText(), - key: XHR, - } - : tab - ); - } - - // const resourcesSize = filtered.reduce((sum, { decodedBodySize }) => sum + (decodedBodySize || 0), 0); - // const transferredSize = filtered - // .reduce((sum, { headerSize, encodedBodySize }) => sum + (headerSize || 0) + (encodedBodySize || 0), 0); - - return ( - - - -
- Network - -
- -
- - {/*
*/} - {/* */} - {/*
{ location }
*/} - {/*
*/} - {/*
*/} - - - 0 } - /> - 0 } - /> - - - - - - {[ - { - label: "Status", - dataKey: 'status', - width: 70, - }, { - label: "Type", - dataKey: 'type', - width: 90, - render: renderType, - }, { - label: "Name", - width: 200, - render: renderName, - }, - { - label: "Size", - width: 60, - render: renderSize, - }, - { - label: "Time", - width: 80, - render: renderDuration, - } - ]} - -
-
-
- ); - } } From ff8ef02c4c3c4a0831cf0010d5e73de6516fec6f Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 13:46:38 +0200 Subject: [PATCH 14/16] change(ui) - no data message for console tab --- .../app/components/Session_/Console/ConsoleContent.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/app/components/Session_/Console/ConsoleContent.js b/frontend/app/components/Session_/Console/ConsoleContent.js index a2c084abd..29820de2e 100644 --- a/frontend/app/components/Session_/Console/ConsoleContent.js +++ b/frontend/app/components/Session_/Console/ConsoleContent.js @@ -86,7 +86,13 @@ export default class ConsoleContent extends React.PureComponent { /> - + + + No {activeTab === ALL ? 'Data' : activeTab.toLowerCase()}} + size="small" + show={filtered.length === 0} + > {filtered.map((l, index) => (
Date: Thu, 11 Aug 2022 13:46:47 +0200 Subject: [PATCH 15/16] change(ui) - no data message for fetch tab --- frontend/app/components/Session_/Fetch/Fetch.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/app/components/Session_/Fetch/Fetch.js b/frontend/app/components/Session_/Fetch/Fetch.js index 2dff482c9..cdfbcc900 100644 --- a/frontend/app/components/Session_/Fetch/Fetch.js +++ b/frontend/app/components/Session_/Fetch/Fetch.js @@ -1,6 +1,6 @@ import React from 'react'; import { getRE } from 'App/utils'; -import { Label, NoContent, Input, SlideModal, CloseButton } from 'UI'; +import { Label, NoContent, Input, SlideModal, CloseButton, Icon } from 'UI'; import { connectPlayer, pause, jump } from 'Player'; // import Autoscroll from '../Autoscroll'; import BottomBlock from '../BottomBlock'; @@ -129,7 +129,17 @@ export default class Fetch extends React.PureComponent {
- + + + No Data + + } + // size="small" + show={filteredList.length === 0} + > + {/* */} {[ { From 04fd2ab0283f32d8e725df60aa3d650d95f0bfb0 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 11 Aug 2022 13:47:23 +0200 Subject: [PATCH 16/16] change(ui) - info circle change --- frontend/app/components/ui/SVG.tsx | 4 +--- frontend/app/svg/icons/info-circle.svg | 13 ++++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/frontend/app/components/ui/SVG.tsx b/frontend/app/components/ui/SVG.tsx index a6df932b4..d5cd23f17 100644 --- a/frontend/app/components/ui/SVG.tsx +++ b/frontend/app/components/ui/SVG.tsx @@ -230,12 +230,11 @@ const SVG = (props: Props) => { case 'hourglass-start': return ; case 'id-card': return ; case 'image': return ; - case 'info-circle': return ; + case 'info-circle': return ; case 'info-square': return ; case 'info': return ; case 'inspect': return ; case 'integrations/assist': return ; - case 'integrations/aws': return ; case 'integrations/bugsnag-text': return ; case 'integrations/bugsnag': return ; case 'integrations/cloudwatch-text': return ; @@ -244,7 +243,6 @@ const SVG = (props: Props) => { case 'integrations/elasticsearch-text': return ; case 'integrations/elasticsearch': return ; case 'integrations/github': return ; - case 'integrations/google-cloud': return ; case 'integrations/graphql': return ; case 'integrations/jira-text': return ; case 'integrations/jira': return ; diff --git a/frontend/app/svg/icons/info-circle.svg b/frontend/app/svg/icons/info-circle.svg index dfb82474d..1dccdda5d 100644 --- a/frontend/app/svg/icons/info-circle.svg +++ b/frontend/app/svg/icons/info-circle.svg @@ -1,4 +1,11 @@ - - - + + + + + + + + + + \ No newline at end of file