change(ui): duck/customMetrics cleanup and upgrades
This commit is contained in:
parent
2c12aa5239
commit
6c56567580
14 changed files with 218 additions and 262 deletions
|
|
@ -10,14 +10,13 @@ import {
|
|||
GLOBAL_DESTINATION_PATH,
|
||||
IFRAME,
|
||||
JWT_PARAM,
|
||||
SPOT_ONBOARDING,
|
||||
SPOT_ONBOARDING
|
||||
} from 'App/constants/storageKeys';
|
||||
import Layout from 'App/layout/Layout';
|
||||
import { withStore } from 'App/mstore';
|
||||
import { useStore, withStore } from 'App/mstore';
|
||||
import { checkParam, handleSpotJWT, isTokenExpired } from 'App/utils';
|
||||
import { ModalProvider } from 'Components/Modal';
|
||||
import { ModalProvider as NewModalProvider } from 'Components/ModalContext';
|
||||
import { fetchListActive as fetchMetadata } from 'Duck/customField';
|
||||
import { setSessionPath } from 'Duck/sessions';
|
||||
import { fetchList as fetchSiteList } from 'Duck/site';
|
||||
import { init as initSite } from 'Duck/site';
|
||||
|
|
@ -43,7 +42,6 @@ interface RouterProps
|
|||
};
|
||||
mstore: any;
|
||||
setJwt: (params: { jwt: string; spotJwt: string | null }) => any;
|
||||
fetchMetadata: (siteId: string) => void;
|
||||
initSite: (site: any) => void;
|
||||
scopeSetup: boolean;
|
||||
localSpotJwt: string | null;
|
||||
|
|
@ -62,8 +60,9 @@ const Router: React.FC<RouterProps> = (props) => {
|
|||
setSessionPath,
|
||||
scopeSetup,
|
||||
localSpotJwt,
|
||||
logout,
|
||||
logout
|
||||
} = props;
|
||||
const { customFieldStore } = useStore();
|
||||
|
||||
const params = new URLSearchParams(location.search);
|
||||
const spotCb = params.get('spotCallback');
|
||||
|
|
@ -175,12 +174,16 @@ const Router: React.FC<RouterProps> = (props) => {
|
|||
}, [isSpotCb, isLoggedIn, localSpotJwt, isSignup]);
|
||||
|
||||
useEffect(() => {
|
||||
if (siteId && siteId !== lastFetchedSiteIdRef.current) {
|
||||
const activeSite = sites.find((s) => s.id == siteId);
|
||||
props.initSite(activeSite);
|
||||
props.fetchMetadata(siteId);
|
||||
lastFetchedSiteIdRef.current = siteId;
|
||||
}
|
||||
const fetchData = async () => {
|
||||
if (siteId && siteId !== lastFetchedSiteIdRef.current) {
|
||||
const activeSite = sites.find((s) => s.id == siteId);
|
||||
props.initSite(activeSite);
|
||||
lastFetchedSiteIdRef.current = activeSite.id;
|
||||
await customFieldStore.fetchListActive(siteId + '');
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [siteId]);
|
||||
|
||||
const lastFetchedSiteIdRef = useRef<any>(null);
|
||||
|
|
@ -232,7 +235,7 @@ const mapStateToProps = (state: Map<string, any>) => {
|
|||
const userInfoLoading = state.getIn([
|
||||
'user',
|
||||
'fetchUserInfoRequest',
|
||||
'loading',
|
||||
'loading'
|
||||
]);
|
||||
const sitesLoading = state.getIn(['site', 'fetchListRequest', 'loading']);
|
||||
const scopeSetup = getScope(state) === 0;
|
||||
|
|
@ -256,7 +259,7 @@ const mapStateToProps = (state: Map<string, any>) => {
|
|||
tenants: state.getIn(['user', 'tenants']),
|
||||
isEnterprise:
|
||||
state.getIn(['user', 'account', 'edition']) === 'ee' ||
|
||||
state.getIn(['user', 'authDetails', 'edition']) === 'ee',
|
||||
state.getIn(['user', 'authDetails', 'edition']) === 'ee'
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -265,9 +268,8 @@ const mapDispatchToProps = {
|
|||
setSessionPath,
|
||||
fetchSiteList,
|
||||
setJwt,
|
||||
fetchMetadata,
|
||||
initSite,
|
||||
logout,
|
||||
logout
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { setSiteId } from 'Duck/site';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { hasSiteId, siteChangeAvailable } from 'App/routes';
|
||||
import { Icon } from 'UI';
|
||||
import { pushNewSite } from 'Duck/user';
|
||||
import { init } from 'Duck/site';
|
||||
import styles from './siteDropdown.module.css';
|
||||
import cn from 'classnames';
|
||||
import { clearSearch } from 'Duck/search';
|
||||
import { clearSearch as clearSearchLive } from 'Duck/liveSearch';
|
||||
import { fetchListActive as fetchIntegrationVariables } from 'Duck/customField';
|
||||
import { withStore } from 'App/mstore';
|
||||
import NewProjectButton from './NewProjectButton';
|
||||
|
||||
@withStore
|
||||
@withRouter
|
||||
@connect(
|
||||
(state) => ({
|
||||
sites: state.getIn(['site', 'list']),
|
||||
siteId: state.getIn(['site', 'siteId']),
|
||||
account: state.getIn(['user', 'account']),
|
||||
}),
|
||||
{
|
||||
setSiteId,
|
||||
pushNewSite,
|
||||
init,
|
||||
clearSearch,
|
||||
clearSearchLive,
|
||||
fetchIntegrationVariables,
|
||||
}
|
||||
)
|
||||
export default class SiteDropdown extends React.PureComponent {
|
||||
state = { showProductModal: false };
|
||||
|
||||
closeModal = (e, newSite) => {
|
||||
this.setState({ showProductModal: false });
|
||||
};
|
||||
|
||||
newSite = () => {
|
||||
this.props.init({});
|
||||
this.setState({ showProductModal: true });
|
||||
};
|
||||
|
||||
switchSite = (siteId) => {
|
||||
const { mstore, location } = this.props;
|
||||
|
||||
this.props.setSiteId(siteId);
|
||||
this.props.fetchIntegrationVariables();
|
||||
this.props.clearSearch(location.pathname.includes('/sessions'));
|
||||
this.props.clearSearchLive();
|
||||
|
||||
mstore.initClient();
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
sites,
|
||||
siteId,
|
||||
account,
|
||||
location: { pathname },
|
||||
} = this.props;
|
||||
const isAdmin = account.admin || account.superAdmin;
|
||||
const activeSite = sites.find((s) => s.id == siteId);
|
||||
const disabled = !siteChangeAvailable(pathname);
|
||||
const showCurrent = hasSiteId(pathname) || siteChangeAvailable(pathname);
|
||||
|
||||
return (
|
||||
<div style={{ width: '180px'}}>
|
||||
<div className={styles.wrapper}>
|
||||
<div className={cn(styles.currentSite)}>
|
||||
{showCurrent && activeSite ? activeSite.host : 'All Projects'}
|
||||
</div>
|
||||
<Icon className={styles.dropdownIcon} color="gray-dark" name="chevron-down" size="16" />
|
||||
<div className={styles.menu}>
|
||||
<ul data-can-disable={disabled}>
|
||||
{isAdmin && <NewProjectButton onClick={this.newSite} isAdmin={isAdmin} />}
|
||||
<div className="border-b border-dashed my-1" />
|
||||
{sites.map((site) => (
|
||||
<li key={site.id} onClick={() => this.switchSite(site.id)}>
|
||||
<Icon name={site.platform === 'web' ? 'browser/browser' : 'mobile'} size="16" />
|
||||
<span className="ml-3">{site.host}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Button, TagBadge } from 'UI';
|
||||
import { connect } from 'react-redux';
|
||||
import CustomFieldForm from '../../../Client/CustomFields/CustomFieldForm';
|
||||
import { confirm } from 'UI';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
||||
interface MetadataListProps {
|
||||
site: { id: string };
|
||||
}
|
||||
|
||||
const MetadataList: React.FC<MetadataListProps> = (props) => {
|
||||
const { site } = props;
|
||||
const { customFieldStore } = useStore();
|
||||
const fields = customFieldStore.list;
|
||||
|
||||
const { showModal, hideModal } = useModal();
|
||||
|
||||
useEffect(() => {
|
||||
customFieldStore.fetchList(site.id);
|
||||
}, [site.id]);
|
||||
|
||||
const save = (field: any) => {
|
||||
customFieldStore.save(site.id, field).then((response) => {
|
||||
if (!response || !response.errors || response.errors.size === 0) {
|
||||
hideModal();
|
||||
toast.success('Metadata added successfully!');
|
||||
} else {
|
||||
toast.error(response.errors[0]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const openModal = () => {
|
||||
showModal(<CustomFieldForm siteId={site.id} onClose={hideModal} onSave={save} />, { right: true });
|
||||
};
|
||||
|
||||
const removeMetadata = async (field: { index: number }) => {
|
||||
if (
|
||||
await confirm({
|
||||
header: 'Metadata',
|
||||
confirmation: 'Are you sure you want to remove?'
|
||||
})
|
||||
) {
|
||||
customFieldStore.remove(site.id, field.index + '');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="py-2 flex">
|
||||
<Button variant="outline" onClick={() => openModal()}>
|
||||
Add Metadata
|
||||
</Button>
|
||||
<div className="flex ml-2">
|
||||
{fields.map((f, index) => (
|
||||
<TagBadge key={index} text={f.key} onRemove={() => removeMetadata(f)} outline />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(
|
||||
(state: any) => ({
|
||||
site: state.getIn(['site', 'instance']),
|
||||
fields: state.getIn(['customFields', 'list']).sortBy((i: any) => i.index),
|
||||
field: state.getIn(['customFields', 'instance']),
|
||||
loading: state.getIn(['customFields', 'fetchRequest', 'loading'])
|
||||
})
|
||||
)(MetadataList);
|
||||
|
|
@ -11,8 +11,8 @@ import Tabs from 'Components/Session/Tabs';
|
|||
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import stl from '../ReplayPlayer/playerBlockHeader.module.css';
|
||||
import { fetchListActive as fetchMetadata } from 'Duck/customField';
|
||||
import { IFRAME } from 'App/constants/storageKeys';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
||||
const SESSIONS_ROUTE = sessionsRoute();
|
||||
|
||||
|
|
@ -23,6 +23,7 @@ function PlayerBlockHeader(props: any) {
|
|||
|
||||
const playerState = store?.get?.() || { width: 0, height: 0, showEvents: false };
|
||||
const { width = 0, height = 0, showEvents = false } = playerState;
|
||||
const { customFieldStore } = useStore();
|
||||
|
||||
const {
|
||||
session,
|
||||
|
|
@ -32,15 +33,13 @@ function PlayerBlockHeader(props: any) {
|
|||
setActiveTab,
|
||||
activeTab,
|
||||
history,
|
||||
sessionPath,
|
||||
fetchMetadata,
|
||||
} = props;
|
||||
|
||||
React.useEffect(() => {
|
||||
const iframe = localStorage.getItem(IFRAME) || false;
|
||||
setHideBack(!!iframe && iframe === 'true');
|
||||
|
||||
if (metaList.size === 0) fetchMetadata();
|
||||
if (metaList.size === 0) customFieldStore.fetchList();
|
||||
}, []);
|
||||
|
||||
const backHandler = () => {
|
||||
|
|
@ -116,7 +115,6 @@ const PlayerHeaderCont = connect(
|
|||
{
|
||||
toggleFavorite,
|
||||
setSessionPath,
|
||||
fetchMetadata,
|
||||
}
|
||||
)(observer(PlayerBlockHeader));
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import Tabs from 'Components/Session/Tabs';
|
|||
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import stl from './playerBlockHeader.module.css';
|
||||
import { fetchListActive as fetchMetadata } from 'Duck/customField';
|
||||
import { IFRAME } from 'App/constants/storageKeys';
|
||||
|
||||
const SESSIONS_ROUTE = sessionsRoute();
|
||||
|
|
@ -25,7 +24,7 @@ const SESSIONS_ROUTE = sessionsRoute();
|
|||
function PlayerBlockHeader(props: any) {
|
||||
const [hideBack, setHideBack] = React.useState(false);
|
||||
const { player, store } = React.useContext(PlayerContext);
|
||||
const { uxtestingStore } = useStore()
|
||||
const { uxtestingStore, customFieldStore } = useStore()
|
||||
const playerState = store?.get?.() || { width: 0, height: 0, showEvents: false }
|
||||
const { width = 0, height = 0, showEvents = false } = playerState
|
||||
|
||||
|
|
@ -39,14 +38,13 @@ function PlayerBlockHeader(props: any) {
|
|||
activeTab,
|
||||
history,
|
||||
sessionPath,
|
||||
fetchMetadata,
|
||||
} = props;
|
||||
|
||||
React.useEffect(() => {
|
||||
const iframe = localStorage.getItem(IFRAME) || false;
|
||||
setHideBack(!!iframe && iframe === 'true');
|
||||
|
||||
if (metaList.size === 0) fetchMetadata();
|
||||
if (metaList.size === 0) customFieldStore.fetchList();
|
||||
}, []);
|
||||
|
||||
const backHandler = () => {
|
||||
|
|
@ -146,7 +144,6 @@ const PlayerHeaderCont = connect(
|
|||
{
|
||||
toggleFavorite,
|
||||
setSessionPath,
|
||||
fetchMetadata,
|
||||
}
|
||||
)(observer(PlayerBlockHeader));
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import { KEYS } from 'Types/filter/customFilter';
|
|||
import { capitalize } from 'App/utils';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { fetchList as fetchMeta } from 'Duck/customField';
|
||||
import AssistSearchField from 'App/components/Assist/AssistSearchField';
|
||||
import LiveSessionSearch from 'Shared/LiveSessionSearch';
|
||||
import cn from 'classnames';
|
||||
|
|
@ -19,7 +18,9 @@ import Session from 'App/mstore/types/session';
|
|||
|
||||
const PER_PAGE = 10;
|
||||
|
||||
interface OwnProps {}
|
||||
interface OwnProps {
|
||||
}
|
||||
|
||||
interface ConnectProps {
|
||||
loading: boolean;
|
||||
metaListLoading: boolean;
|
||||
|
|
@ -34,30 +35,30 @@ interface ConnectProps {
|
|||
updateCurrentPage: (page: number) => void;
|
||||
applyFilter: (filter: any) => void;
|
||||
onAdd: () => void;
|
||||
fetchMeta: () => void;
|
||||
}
|
||||
|
||||
type Props = OwnProps & ConnectProps;
|
||||
|
||||
function AssistSessionsModal(props: Props) {
|
||||
const { assistMultiviewStore } = useStore();
|
||||
const { loading, list, metaList = [], filter, currentPage, total, onAdd, fetchMeta } = props;
|
||||
const { assistMultiviewStore, customFieldStore } = useStore();
|
||||
const { loading, list, filter, currentPage, total, onAdd } = props;
|
||||
const onUserClick = () => false;
|
||||
const { filters } = filter;
|
||||
const hasUserFilter = filters.map((i: any) => i.key).includes(KEYS.USERID);
|
||||
const metaList = customFieldStore.list;
|
||||
|
||||
const sortOptions = metaList
|
||||
.map((i: any) => ({
|
||||
label: capitalize(i),
|
||||
value: i,
|
||||
}))
|
||||
.toJS();
|
||||
value: i
|
||||
}));
|
||||
|
||||
React.useEffect(() => {
|
||||
if (total === 0) {
|
||||
reloadSessions();
|
||||
}
|
||||
fetchMeta();
|
||||
// fetchMeta();
|
||||
customFieldStore.fetchList();
|
||||
}, []);
|
||||
const reloadSessions = () => props.applyFilter({ ...filter });
|
||||
const onSortChange = ({ value }: any) => {
|
||||
|
|
@ -109,37 +110,37 @@ function AssistSessionsModal(props: Props) {
|
|||
<LiveSessionSearch />
|
||||
<div className="my-4" />
|
||||
<Loader loading={loading}>
|
||||
<div className={'overflow-y-scroll'} style={{ maxHeight: '85vh'}}>
|
||||
{list.map((session) => (
|
||||
<React.Fragment key={session.sessionId}>
|
||||
<div
|
||||
className={cn(
|
||||
'rounded bg-white mb-2 overflow-hidden border',
|
||||
assistMultiviewStore.sessions.findIndex(
|
||||
(s: Record<string, any>) => s.sessionId === session.sessionId
|
||||
) !== -1
|
||||
? 'cursor-not-allowed opacity-60'
|
||||
: ''
|
||||
)}
|
||||
>
|
||||
<SessionItem
|
||||
key={session.sessionId}
|
||||
session={session}
|
||||
live
|
||||
hasUserFilter={hasUserFilter}
|
||||
onUserClick={onUserClick}
|
||||
metaList={metaList}
|
||||
isDisabled={
|
||||
<div className={'overflow-y-scroll'} style={{ maxHeight: '85vh' }}>
|
||||
{list.map((session) => (
|
||||
<React.Fragment key={session.sessionId}>
|
||||
<div
|
||||
className={cn(
|
||||
'rounded bg-white mb-2 overflow-hidden border',
|
||||
assistMultiviewStore.sessions.findIndex(
|
||||
(s: Record<string, any>) => s.sessionId === session.sessionId
|
||||
) !== -1
|
||||
}
|
||||
isAdd
|
||||
onClick={() => onSessionAdd(session)}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
))}
|
||||
? 'cursor-not-allowed opacity-60'
|
||||
: ''
|
||||
)}
|
||||
>
|
||||
<SessionItem
|
||||
key={session.sessionId}
|
||||
session={session}
|
||||
live
|
||||
hasUserFilter={hasUserFilter}
|
||||
onUserClick={onUserClick}
|
||||
metaList={metaList}
|
||||
isDisabled={
|
||||
assistMultiviewStore.sessions.findIndex(
|
||||
(s: Record<string, any>) => s.sessionId === session.sessionId
|
||||
) !== -1
|
||||
}
|
||||
isAdd
|
||||
onClick={() => onSessionAdd(session)}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</Loader>
|
||||
|
||||
|
|
@ -166,12 +167,11 @@ export default connect(
|
|||
total: state.getIn(['liveSearch', 'total']),
|
||||
currentPage: state.getIn(['liveSearch', 'currentPage']),
|
||||
metaList: state.getIn(['customFields', 'list']).map((i: any) => i.key),
|
||||
sort: state.getIn(['liveSearch', 'sort']),
|
||||
sort: state.getIn(['liveSearch', 'sort'])
|
||||
}),
|
||||
{
|
||||
applyFilter,
|
||||
addFilterByKeyAndValue,
|
||||
updateCurrentPage,
|
||||
fetchMeta,
|
||||
updateCurrentPage
|
||||
}
|
||||
)(observer(AssistSessionsModal));
|
||||
|
|
|
|||
|
|
@ -8,11 +8,10 @@ import React from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
|
||||
import { withStore } from 'App/mstore';
|
||||
import { useStore, withStore } from 'App/mstore';
|
||||
import { hasSiteId, siteChangeAvailable } from 'App/routes';
|
||||
import NewSiteForm from 'Components/Client/Sites/NewSiteForm';
|
||||
import { useModal } from 'Components/Modal';
|
||||
import { fetchListActive as fetchMetadata } from 'Duck/customField';
|
||||
import { clearSearch as clearSearchLive } from 'Duck/liveSearch';
|
||||
import { clearSearch } from 'Duck/search';
|
||||
import { setSiteId } from 'Duck/site';
|
||||
|
|
@ -31,7 +30,6 @@ interface Props extends RouteComponentProps {
|
|||
sites: Site[];
|
||||
siteId: string;
|
||||
setSiteId: (siteId: string) => void;
|
||||
fetchMetadata: () => void;
|
||||
clearSearch: (isSession: boolean) => void;
|
||||
clearSearchLive: () => void;
|
||||
initProject: (data: any) => void;
|
||||
|
|
@ -46,10 +44,11 @@ function ProjectDropdown(props: Props) {
|
|||
const showCurrent =
|
||||
hasSiteId(location.pathname) || siteChangeAvailable(location.pathname);
|
||||
const { showModal, hideModal } = useModal();
|
||||
const { customFieldStore } = useStore();
|
||||
|
||||
const handleSiteChange = (newSiteId: string) => {
|
||||
const handleSiteChange = async (newSiteId: string) => {
|
||||
props.setSiteId(newSiteId); // Fixed: should set the new siteId, not the existing one
|
||||
props.fetchMetadata();
|
||||
await customFieldStore.fetchList(newSiteId)
|
||||
props.clearSearch(location.pathname.includes('/sessions'));
|
||||
props.clearSearchLive();
|
||||
|
||||
|
|
@ -151,7 +150,6 @@ const mapStateToProps = (state: any) => ({
|
|||
export default withRouter(
|
||||
connect(mapStateToProps, {
|
||||
setSiteId,
|
||||
fetchMetadata,
|
||||
clearSearch,
|
||||
clearSearchLive,
|
||||
initProject,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import {
|
|||
checkForLatestSessions
|
||||
} from 'Duck/search';
|
||||
import { numberWithCommas } from 'App/utils';
|
||||
import { fetchListActive as fetchMetadata } from 'Duck/customField';
|
||||
import { toggleFavorite } from 'Duck/sessions';
|
||||
import SessionDateRange from './SessionDateRange';
|
||||
import RecordingStatus from 'Shared/SessionsTabOverview/components/RecordingStatus';
|
||||
|
|
@ -51,7 +50,6 @@ interface Props extends RouteComponentProps {
|
|||
updateCurrentPage: (page: number) => void;
|
||||
setScrollPosition: (scrollPosition: number) => void;
|
||||
fetchSessions: (filters: any, force: boolean) => void;
|
||||
fetchMetadata: () => void;
|
||||
updateProjectRecordingStatus: (siteId: string, status: boolean) => void;
|
||||
activeTab: any;
|
||||
isEnterprise?: boolean;
|
||||
|
|
@ -87,6 +85,7 @@ function SessionList(props: Props) {
|
|||
const activeSite: any = sites.find((s: any) => s.id === siteId);
|
||||
const hasNoRecordings = !activeSite || !activeSite.recorded;
|
||||
|
||||
|
||||
const NO_CONTENT = React.useMemo(() => {
|
||||
if (isBookmark && !isEnterprise) {
|
||||
setNoContentType(NoContentType.Bookmarked);
|
||||
|
|
@ -165,7 +164,6 @@ function SessionList(props: Props) {
|
|||
props.fetchSessions(null, true);
|
||||
}, 300);
|
||||
}
|
||||
// props.fetchMetadata();
|
||||
|
||||
return () => {
|
||||
props.setScrollPosition(window.scrollY);
|
||||
|
|
@ -304,7 +302,6 @@ export default connect(
|
|||
addFilterByKeyAndValue,
|
||||
setScrollPosition,
|
||||
fetchSessions,
|
||||
fetchMetadata,
|
||||
checkForLatestSessions,
|
||||
toggleFavorite,
|
||||
updateProjectRecordingStatus
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import SideMenu from 'App/layout/SideMenu';
|
|||
import TopHeader from 'App/layout/TopHeader';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { fetchListActive as fetchMetadata } from 'Duck/customField';
|
||||
import { init as initSite } from 'Duck/site';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
|
|
@ -15,7 +14,6 @@ interface Props {
|
|||
children: React.ReactNode;
|
||||
hideHeader?: boolean;
|
||||
siteId?: string;
|
||||
fetchMetadata: (siteId: string) => void;
|
||||
initSite: (site: any) => void;
|
||||
sites: any[];
|
||||
}
|
||||
|
|
@ -25,18 +23,6 @@ function Layout(props: Props) {
|
|||
const isPlayer = /\/(session|assist|view-spot)\//.test(window.location.pathname);
|
||||
const { settingsStore } = useStore();
|
||||
|
||||
// const lastFetchedSiteIdRef = React.useRef<string | null>(null);
|
||||
//
|
||||
// useEffect(() => {
|
||||
// if (!siteId || siteId === lastFetchedSiteIdRef.current) return;
|
||||
//
|
||||
// const activeSite = props.sites.find((s) => s.id == siteId);
|
||||
// props.initSite(activeSite);
|
||||
// props.fetchMetadata(siteId);
|
||||
//
|
||||
// lastFetchedSiteIdRef.current = siteId;
|
||||
// }, [siteId]);
|
||||
|
||||
return (
|
||||
<AntLayout style={{ minHeight: '100vh' }}>
|
||||
{!hideHeader && (
|
||||
|
|
@ -69,4 +55,4 @@ function Layout(props: Props) {
|
|||
export default connect((state: any) => ({
|
||||
siteId: state.getIn(['site', 'siteId']),
|
||||
sites: state.getIn(['site', 'list'])
|
||||
}), { fetchMetadata, initSite })(observer(Layout));
|
||||
}), { initSite })(observer(Layout));
|
||||
|
|
|
|||
|
|
@ -9,14 +9,12 @@ import { INDEXES } from 'App/constants/zindex';
|
|||
import { connect } from 'react-redux';
|
||||
import { logout } from 'Duck/user';
|
||||
import { init as initSite } from 'Duck/site';
|
||||
import { fetchListActive as fetchMetadata } from 'Duck/customField';
|
||||
|
||||
const { Header } = Layout;
|
||||
|
||||
interface Props {
|
||||
account: any;
|
||||
siteId: string;
|
||||
fetchMetadata: (siteId: string) => void;
|
||||
initSite: (site: any) => void;
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +47,7 @@ function TopHeader(props: Props) {
|
|||
alignItems: 'center',
|
||||
height: '60px'
|
||||
}}
|
||||
className='justify-between'
|
||||
className="justify-between"
|
||||
>
|
||||
<Space>
|
||||
<div
|
||||
|
|
@ -57,14 +55,14 @@ function TopHeader(props: Props) {
|
|||
settingsStore.updateMenuCollapsed(!settingsStore.menuCollapsed);
|
||||
}}
|
||||
style={{ paddingTop: '4px' }}
|
||||
className='cursor-pointer'
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Tooltip title={settingsStore.menuCollapsed ? 'Show Menu' : 'Hide Menu'} mouseEnterDelay={1}>
|
||||
<Icon name={settingsStore.menuCollapsed ? 'side_menu_closed' : 'side_menu_open'} size={20} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center'>
|
||||
<div className="flex items-center">
|
||||
<Logo siteId={siteId} />
|
||||
</div>
|
||||
</Space>
|
||||
|
|
@ -81,8 +79,7 @@ const mapStateToProps = (state: any) => ({
|
|||
|
||||
const mapDispatchToProps = {
|
||||
onLogoutClick: logout,
|
||||
initSite,
|
||||
fetchMetadata
|
||||
initSite
|
||||
};
|
||||
|
||||
export default connect(
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ import customFields from 'Components/Client/CustomFields';
|
|||
|
||||
class CustomFieldStore {
|
||||
isLoading: boolean = false;
|
||||
isAllLoading: boolean = false;
|
||||
isSaving: boolean = false;
|
||||
list: CustomField[] = [];
|
||||
allList: CustomField[] = [];
|
||||
instance: CustomField = new CustomField();
|
||||
sources: CustomField[] = [];
|
||||
fetchedMetadata: boolean = false;
|
||||
|
|
@ -43,7 +45,7 @@ class CustomFieldStore {
|
|||
async fetchListActive(siteId?: string): Promise<any> {
|
||||
this.isLoading = true;
|
||||
try {
|
||||
const response = await customFieldService.get(siteId);
|
||||
const response = await customFieldService.fetchList(siteId);
|
||||
clearMetaFilters();
|
||||
response.forEach((item: any) => {
|
||||
addElementToFiltersMap(FilterCategory.METADATA, '_' + item.key);
|
||||
|
|
|
|||
|
|
@ -1,29 +1,29 @@
|
|||
import BaseService from './BaseService';
|
||||
|
||||
export default class CustomFieldService extends BaseService {
|
||||
async fetchList(siteId: string): Promise<any> {
|
||||
return this.client.get(siteId ? `/${siteId}/metadata` : '/metadata')
|
||||
async fetchList(projectId?: string): Promise<any> {
|
||||
return this.client.get(projectId ? `/${projectId}/metadata` : '/metadata')
|
||||
.then(r => r.json()).then(j => j.data);
|
||||
}
|
||||
|
||||
async get(siteId?: string): Promise<any> {
|
||||
const url = siteId ? `/${siteId}/metadata` : '/metadata';
|
||||
async get(projectId?: string): Promise<any> {
|
||||
const url = projectId ? `/${projectId}/metadata` : '/PROJECT_ID/metadata';
|
||||
return this.client.get(url)
|
||||
.then(r => r.json()).then(j => j.data);
|
||||
}
|
||||
|
||||
async create(siteId: string, customField: any): Promise<any> {
|
||||
return this.client.post(`/${siteId}/metadata`, customField)
|
||||
async create(projectId: string, customField: any): Promise<any> {
|
||||
return this.client.post(`/${projectId}/metadata`, customField)
|
||||
.then(r => r.json()).then(j => j.data);
|
||||
}
|
||||
|
||||
async update(siteId: string, instance: any): Promise<any> {
|
||||
return this.client.put(`/${siteId}/metadata/${instance.index}`, instance)
|
||||
async update(projectId: string, instance: any): Promise<any> {
|
||||
return this.client.put(`/${projectId}/metadata/${instance.index}`, instance)
|
||||
.then(r => r.json()).then(j => j.data);
|
||||
}
|
||||
|
||||
async delete(siteId: string, index: string): Promise<any> {
|
||||
return this.client.delete(`/${siteId}/metadata/${index}`)
|
||||
async delete(projectId: string, index: string): Promise<any> {
|
||||
return this.client.delete(`/${projectId}/metadata/${index}`)
|
||||
.then(r => r.json()).then(j => j.data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ export default Record({
|
|||
key: '',
|
||||
operator: 'is',
|
||||
label: '',
|
||||
icon: '',
|
||||
type: '',
|
||||
value: [""],
|
||||
custom: '',
|
||||
|
|
|
|||
|
|
@ -109,68 +109,68 @@ export default Record({
|
|||
}
|
||||
});
|
||||
|
||||
export const preloadedFilters = [];
|
||||
// export const preloadedFilters = [];
|
||||
|
||||
export const defaultFilters = [
|
||||
{
|
||||
category: 'Interactions',
|
||||
type: 'default',
|
||||
keys: [
|
||||
{ label: 'Click', key: KEYS.CLICK, type: KEYS.CLICK, filterKey: KEYS.CLICK, icon: 'filters/click', isFilter: false },
|
||||
{ label: 'DOM Complete', key: KEYS.DOM_COMPLETE, type: KEYS.DOM_COMPLETE, filterKey: KEYS.DOM_COMPLETE, icon: 'filters/click', isFilter: false },
|
||||
{ label: 'Largest Contentful Paint Time', key: KEYS.LARGEST_CONTENTFUL_PAINT_TIME, type: KEYS.LARGEST_CONTENTFUL_PAINT_TIME, filterKey: KEYS.LARGEST_CONTENTFUL_PAINT_TIME, icon: 'filters/click', isFilter: false },
|
||||
{ label: 'Time Between Events', key: KEYS.TIME_BETWEEN_EVENTS, type: KEYS.TIME_BETWEEN_EVENTS, filterKey: KEYS.TIME_BETWEEN_EVENTS, icon: 'filters/click', isFilter: false },
|
||||
{ label: 'Avg CPU Load', key: KEYS.AVG_CPU_LOAD, type: KEYS.AVG_CPU_LOAD, filterKey: KEYS.AVG_CPU_LOAD, icon: 'filters/click', isFilter: false },
|
||||
{ label: 'Memory Usage', key: KEYS.AVG_MEMORY_USAGE, type: KEYS.AVG_MEMORY_USAGE, filterKey: KEYS.AVG_MEMORY_USAGE, icon: 'filters/click', isFilter: false },
|
||||
{ label: 'Input', key: KEYS.INPUT, type: KEYS.INPUT, filterKey: KEYS.INPUT, icon: 'event/input', isFilter: false },
|
||||
{ label: 'Path', key: KEYS.LOCATION, type: KEYS.LOCATION, filterKey: KEYS.LOCATION, icon: 'event/link', isFilter: false },
|
||||
// { label: 'View', key: KEYS.VIEW, type: KEYS.VIEW, filterKey: KEYS.VIEW, icon: 'event/view', isFilter: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
category: 'Gear',
|
||||
type: 'default',
|
||||
keys: [
|
||||
{ label: 'OS', key: KEYS.USER_OS, type: KEYS.USER_OS, filterKey: KEYS.USER_OS, icon: 'os', isFilter: true },
|
||||
{ label: 'Browser', key: KEYS.USER_BROWSER, type: KEYS.USER_BROWSER, filterKey: KEYS.USER_BROWSER, icon: 'window', isFilter: true },
|
||||
{ label: 'Device', key: KEYS.USER_DEVICE, type: KEYS.USER_DEVICE, filterKey: KEYS.USER_DEVICE, icon: 'device', isFilter: true },
|
||||
{ label: 'Rev ID', key: KEYS.REVID, type: KEYS.REVID, filterKey: KEYS.REVID, icon: 'filters/rev-id', isFilter: true },
|
||||
{ label: 'Platform', key: KEYS.PLATFORM, type: KEYS.PLATFORM, filterKey: KEYS.PLATFORM, icon: 'filters/platform', isFilter: true },
|
||||
]
|
||||
},
|
||||
{
|
||||
category: 'Recording Attributes',
|
||||
type: 'default',
|
||||
keys: [
|
||||
{ label: 'Referrer', key: KEYS.REFERRER, type: KEYS.REFERRER, filterKey: KEYS.REFERRER, icon: 'chat-square-quote', isFilter: true },
|
||||
{ label: 'Duration', key: KEYS.DURATION, type: KEYS.DURATION, filterKey: KEYS.DURATION, icon: 'clock', isFilter: true },
|
||||
{ label: 'Country', key: KEYS.USER_COUNTRY, type: KEYS.USER_COUNTRY, filterKey: KEYS.USER_COUNTRY, icon: 'map-marker-alt', isFilter: true },
|
||||
]
|
||||
},
|
||||
{
|
||||
category: 'Javascript',
|
||||
type: 'default',
|
||||
keys: [
|
||||
{ label: 'Errors', key: KEYS.ERROR, type: KEYS.ERROR, filterKey: KEYS.ERROR, icon: 'exclamation-circle', isFilter: false },
|
||||
{ label: 'Issues', key: KEYS.ISSUES, type: KEYS.ISSUES, filterKey: KEYS.ISSUES, icon: 'exclamation-circle', isFilter: true },
|
||||
{ label: 'UTM Source', key: KEYS.UTM_SOURCE, type: KEYS.UTM_SOURCE, filterKey: KEYS.UTM_SOURCE, icon: 'exclamation-circle', isFilter: true },
|
||||
{ label: 'UTM Medium', key: KEYS.UTM_MEDIUM, type: KEYS.UTM_MEDIUM, filterKey: KEYS.UTM_MEDIUM, icon: 'exclamation-circle', isFilter: true },
|
||||
{ label: 'UTM Campaign', key: KEYS.UTM_CAMPAIGN, type: KEYS.UTM_CAMPAIGN, filterKey: KEYS.UTM_CAMPAIGN, icon: 'exclamation-circle', isFilter: true },
|
||||
{ label: 'Fetch Requests', key: KEYS.FETCH, type: KEYS.FETCH, filterKey: KEYS.FETCH, icon: 'fetch', isFilter: false },
|
||||
{ label: 'GraphQL Queries', key: KEYS.GRAPHQL, type: KEYS.GRAPHQL, filterKey: KEYS.GRAPHQL, icon: 'vendors/graphql', isFilter: false },
|
||||
{ label: 'Store Actions', key: KEYS.STATEACTION, type: KEYS.STATEACTION, filterKey: KEYS.STATEACTION, icon: 'store', isFilter: false },
|
||||
{ label: 'Custom Events', key: KEYS.CUSTOM, type: KEYS.CUSTOM, filterKey: KEYS.CUSTOM, icon: 'filters/file-code', isFilter: false },
|
||||
]
|
||||
},
|
||||
{
|
||||
category: 'User',
|
||||
type: 'default',
|
||||
keys: [
|
||||
{ label: 'User ID', key: KEYS.USERID, type: KEYS.USERID, filterKey: KEYS.USERID, icon: 'filters/user-alt', isFilter: true },
|
||||
{ label: 'Anonymous ID', key: KEYS.USERANONYMOUSID, type: KEYS.USERANONYMOUSID, filterKey: KEYS.USERANONYMOUSID, icon: 'filters/userid', isFilter: true },
|
||||
]
|
||||
}
|
||||
];
|
||||
// export const defaultFilters = [
|
||||
// {
|
||||
// category: 'Interactions',
|
||||
// type: 'default',
|
||||
// keys: [
|
||||
// { label: 'Click', key: KEYS.CLICK, type: KEYS.CLICK, filterKey: KEYS.CLICK, icon: 'filters/click', isFilter: false },
|
||||
// { label: 'DOM Complete', key: KEYS.DOM_COMPLETE, type: KEYS.DOM_COMPLETE, filterKey: KEYS.DOM_COMPLETE, icon: 'filters/click', isFilter: false },
|
||||
// { label: 'Largest Contentful Paint Time', key: KEYS.LARGEST_CONTENTFUL_PAINT_TIME, type: KEYS.LARGEST_CONTENTFUL_PAINT_TIME, filterKey: KEYS.LARGEST_CONTENTFUL_PAINT_TIME, icon: 'filters/click', isFilter: false },
|
||||
// { label: 'Time Between Events', key: KEYS.TIME_BETWEEN_EVENTS, type: KEYS.TIME_BETWEEN_EVENTS, filterKey: KEYS.TIME_BETWEEN_EVENTS, icon: 'filters/click', isFilter: false },
|
||||
// { label: 'Avg CPU Load', key: KEYS.AVG_CPU_LOAD, type: KEYS.AVG_CPU_LOAD, filterKey: KEYS.AVG_CPU_LOAD, icon: 'filters/click', isFilter: false },
|
||||
// { label: 'Memory Usage', key: KEYS.AVG_MEMORY_USAGE, type: KEYS.AVG_MEMORY_USAGE, filterKey: KEYS.AVG_MEMORY_USAGE, icon: 'filters/click', isFilter: false },
|
||||
// { label: 'Input', key: KEYS.INPUT, type: KEYS.INPUT, filterKey: KEYS.INPUT, icon: 'event/input', isFilter: false },
|
||||
// { label: 'Path', key: KEYS.LOCATION, type: KEYS.LOCATION, filterKey: KEYS.LOCATION, icon: 'event/link', isFilter: false },
|
||||
// // { label: 'View', key: KEYS.VIEW, type: KEYS.VIEW, filterKey: KEYS.VIEW, icon: 'event/view', isFilter: false }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// category: 'Gear',
|
||||
// type: 'default',
|
||||
// keys: [
|
||||
// { label: 'OS', key: KEYS.USER_OS, type: KEYS.USER_OS, filterKey: KEYS.USER_OS, icon: 'os', isFilter: true },
|
||||
// { label: 'Browser', key: KEYS.USER_BROWSER, type: KEYS.USER_BROWSER, filterKey: KEYS.USER_BROWSER, icon: 'window', isFilter: true },
|
||||
// { label: 'Device', key: KEYS.USER_DEVICE, type: KEYS.USER_DEVICE, filterKey: KEYS.USER_DEVICE, icon: 'device', isFilter: true },
|
||||
// { label: 'Rev ID', key: KEYS.REVID, type: KEYS.REVID, filterKey: KEYS.REVID, icon: 'filters/rev-id', isFilter: true },
|
||||
// { label: 'Platform', key: KEYS.PLATFORM, type: KEYS.PLATFORM, filterKey: KEYS.PLATFORM, icon: 'filters/platform', isFilter: true },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// category: 'Recording Attributes',
|
||||
// type: 'default',
|
||||
// keys: [
|
||||
// { label: 'Referrer', key: KEYS.REFERRER, type: KEYS.REFERRER, filterKey: KEYS.REFERRER, icon: 'chat-square-quote', isFilter: true },
|
||||
// { label: 'Duration', key: KEYS.DURATION, type: KEYS.DURATION, filterKey: KEYS.DURATION, icon: 'clock', isFilter: true },
|
||||
// { label: 'Country', key: KEYS.USER_COUNTRY, type: KEYS.USER_COUNTRY, filterKey: KEYS.USER_COUNTRY, icon: 'map-marker-alt', isFilter: true },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// category: 'Javascript',
|
||||
// type: 'default',
|
||||
// keys: [
|
||||
// { label: 'Errors', key: KEYS.ERROR, type: KEYS.ERROR, filterKey: KEYS.ERROR, icon: 'exclamation-circle', isFilter: false },
|
||||
// { label: 'Issues', key: KEYS.ISSUES, type: KEYS.ISSUES, filterKey: KEYS.ISSUES, icon: 'exclamation-circle', isFilter: true },
|
||||
// { label: 'UTM Source', key: KEYS.UTM_SOURCE, type: KEYS.UTM_SOURCE, filterKey: KEYS.UTM_SOURCE, icon: 'exclamation-circle', isFilter: true },
|
||||
// { label: 'UTM Medium', key: KEYS.UTM_MEDIUM, type: KEYS.UTM_MEDIUM, filterKey: KEYS.UTM_MEDIUM, icon: 'exclamation-circle', isFilter: true },
|
||||
// { label: 'UTM Campaign', key: KEYS.UTM_CAMPAIGN, type: KEYS.UTM_CAMPAIGN, filterKey: KEYS.UTM_CAMPAIGN, icon: 'exclamation-circle', isFilter: true },
|
||||
// { label: 'Fetch Requests', key: KEYS.FETCH, type: KEYS.FETCH, filterKey: KEYS.FETCH, icon: 'fetch', isFilter: false },
|
||||
// { label: 'GraphQL Queries', key: KEYS.GRAPHQL, type: KEYS.GRAPHQL, filterKey: KEYS.GRAPHQL, icon: 'vendors/graphql', isFilter: false },
|
||||
// { label: 'Store Actions', key: KEYS.STATEACTION, type: KEYS.STATEACTION, filterKey: KEYS.STATEACTION, icon: 'store', isFilter: false },
|
||||
// { label: 'Custom Events', key: KEYS.CUSTOM, type: KEYS.CUSTOM, filterKey: KEYS.CUSTOM, icon: 'filters/file-code', isFilter: false },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// category: 'User',
|
||||
// type: 'default',
|
||||
// keys: [
|
||||
// { label: 'User ID', key: KEYS.USERID, type: KEYS.USERID, filterKey: KEYS.USERID, icon: 'filters/user-alt', isFilter: true },
|
||||
// { label: 'Anonymous ID', key: KEYS.USERANONYMOUSID, type: KEYS.USERANONYMOUSID, filterKey: KEYS.USERANONYMOUSID, icon: 'filters/userid', isFilter: true },
|
||||
// ]
|
||||
// }
|
||||
// ];
|
||||
|
||||
export const getEventIcon = (filter) => {
|
||||
let { type, key, source } = filter;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue