diff --git a/frontend/app/components/Client/Integrations/IntegrationModalCard.tsx b/frontend/app/components/Client/Integrations/IntegrationModalCard.tsx
index 4864b092b..37ff588f0 100644
--- a/frontend/app/components/Client/Integrations/IntegrationModalCard.tsx
+++ b/frontend/app/components/Client/Integrations/IntegrationModalCard.tsx
@@ -1,19 +1,19 @@
import React from 'react';
import { Icon } from 'UI';
-import DocLink from 'Shared/DocLink';
interface Props {
title: string;
icon: string;
description: string;
+ useIcon?: boolean;
}
function IntegrationModalCard(props: Props) {
- const { title, icon, description } = props;
+ const { title, icon, description, useIcon } = props;
return (
-

+ {useIcon ?
:

}
{title}
diff --git a/frontend/app/components/Client/Integrations/Integrations.tsx b/frontend/app/components/Client/Integrations/Integrations.tsx
index a39a533d5..b00e5a0b7 100644
--- a/frontend/app/components/Client/Integrations/Integrations.tsx
+++ b/frontend/app/components/Client/Integrations/Integrations.tsx
@@ -1,5 +1,4 @@
import withPageTitle from 'HOCs/withPageTitle';
-import cn from 'classnames';
import { observer } from 'mobx-react-lite';
import React, { useEffect, useState } from 'react';
@@ -9,30 +8,26 @@ import IntegrationFilters from 'Components/Client/Integrations/IntegrationFilter
import { PageTitle } from 'UI';
import DocCard from 'Shared/DocCard/DocCard';
+import SiteDropdown from 'Shared/SiteDropdown';
-import AssistDoc from './AssistDoc';
-import BugsnagForm from './BugsnagForm';
-import CloudwatchForm from './CloudwatchForm';
-import DatadogForm from './DatadogForm';
-import ElasticsearchForm from './ElasticsearchForm';
+import DatadogForm from './Backend/DatadogForm/DatadogFormModal';
+import DynatraceFormModal from './Backend/DynatraceForm/DynatraceFormModal';
+import ElasticsearchForm from './Backend/ElasticForm/ElasticFormModal';
+import SentryForm from './Backend/SentryForm/SentryFormModal';
import GithubForm from './GithubForm';
-import GraphQLDoc from './GraphQLDoc';
import IntegrationItem from './IntegrationItem';
import JiraForm from './JiraForm';
-import MobxDoc from './MobxDoc';
-import NewrelicForm from './NewrelicForm';
-import NgRxDoc from './NgRxDoc';
-import PiniaDoc from './PiniaDoc';
import ProfilerDoc from './ProfilerDoc';
-import ReduxDoc from './ReduxDoc';
-import RollbarForm from './RollbarForm';
-import SentryForm from './SentryForm';
import SlackForm from './SlackForm';
-import StackdriverForm from './StackdriverForm';
-import SumoLogicForm from './SumoLogicForm';
import MSTeams from './Teams';
-import VueDoc from './VueDoc';
-import ZustandDoc from './ZustandDoc';
+import AssistDoc from './Tracker/AssistDoc';
+import GraphQLDoc from './Tracker/GraphQLDoc';
+import MobxDoc from './Tracker/MobxDoc';
+import NgRxDoc from './Tracker/NgRxDoc';
+import PiniaDoc from './Tracker/PiniaDoc';
+import ReduxDoc from './Tracker/ReduxDoc';
+import VueDoc from './Tracker/VueDoc';
+import ZustandDoc from './Tracker/ZustandDoc';
interface Props {
siteId: string;
@@ -41,23 +36,27 @@ interface Props {
function Integrations(props: Props) {
const { integrationsStore, projectsStore } = useStore();
- const siteId = projectsStore.siteId;
+ const initialSiteId = projectsStore.siteId;
+ const siteId = integrationsStore.integrations.siteId;
const fetchIntegrationList = integrationsStore.integrations.fetchIntegrations;
const storeIntegratedList = integrationsStore.integrations.list;
const { hideHeader = false } = props;
- const { showModal } = useModal();
+ const { showModal, hideModal } = useModal();
const [integratedList, setIntegratedList] = useState
([]);
const [activeFilter, setActiveFilter] = useState('all');
useEffect(() => {
- const list = storeIntegratedList
- .filter((item: any) => item.integrated)
+ const list = integrationsStore.integrations.integratedServices
.map((item: any) => item.name);
setIntegratedList(list);
}, [storeIntegratedList]);
useEffect(() => {
- void fetchIntegrationList(siteId);
+ if (siteId) {
+ void fetchIntegrationList(siteId);
+ } else if (initialSiteId) {
+ integrationsStore.integrations.setSiteId(initialSiteId);
+ }
}, [siteId]);
const onClick = (integration: any, width: number) => {
@@ -84,6 +83,8 @@ function Integrations(props: Props) {
showModal(
React.cloneElement(integration.component, {
integrated: integratedList.includes(integration.slug),
+ siteId,
+ onClose: hideModal,
}),
{ right: true, width }
);
@@ -112,15 +113,17 @@ function Integrations(props: Props) {
(cat) => cat.integrations
);
- console.log(
- allIntegrations,
- integratedList
- )
+ const onChangeSelect = ({ value }: any) => {
+ integrationsStore.integrations.setSiteId(value.value);
+ };
+
return (
<>
- {!hideHeader &&
Integrations } />}
-
+
+ {!hideHeader &&
Integrations } />}
+
+
- {allIntegrations.map((integration: any) => (
-
- onClick(
- integration,
- filteredIntegrations.find((cat) =>
- cat.integrations.includes(integration)
- ).title === 'Plugins'
- ? 500
- : 350
- )
- }
- hide={
- (integration.slug === 'github' &&
- integratedList.includes('jira')) ||
- (integration.slug === 'jira' && integratedList.includes('github'))
- }
- />
+ {allIntegrations.map((integration, i) => (
+
+
+ onClick(
+ integration,
+ filteredIntegrations.find((cat) =>
+ cat.integrations.includes(integration)
+ )?.title === 'Plugins'
+ ? 500
+ : 350
+ )
+ }
+ hide={
+ (integration.slug === 'github' &&
+ integratedList.includes('jira')) ||
+ (integration.slug === 'jira' &&
+ integratedList.includes('github'))
+ }
+ />
+
))}
>
);
}
-export default withPageTitle('Integrations - OpenReplay Preferences')(observer(Integrations))
+export default withPageTitle('Integrations - OpenReplay Preferences')(
+ observer(Integrations)
+);
const integrations = [
{
@@ -219,22 +226,6 @@ const integrations = [
icon: 'integrations/sentry',
component:
,
},
- {
- title: 'Bugsnag',
- subtitle:
- 'Integrate Bugsnag to access the OpenReplay session linked to the JS exception within its interface.',
- slug: 'bugsnag',
- icon: 'integrations/bugsnag',
- component:
,
- },
- {
- title: 'Rollbar',
- subtitle:
- 'Integrate Rollbar with session replays to seamlessly observe backend errors.',
- slug: 'rollbar',
- icon: 'integrations/rollbar',
- component:
,
- },
{
title: 'Elasticsearch',
subtitle:
@@ -252,36 +243,13 @@ const integrations = [
component:
,
},
{
- title: 'Sumo Logic',
+ title: 'Dynatrace',
subtitle:
- 'Integrate Sumo Logic with session replays to seamlessly observe backend errors.',
- slug: 'sumologic',
- icon: 'integrations/sumologic',
- component:
,
- },
- {
- title: 'Google Cloud',
- subtitle:
- 'Integrate Google Cloud to view backend logs and errors in conjunction with session replay',
- slug: 'stackdriver',
- icon: 'integrations/google-cloud',
- component:
,
- },
- {
- title: 'CloudWatch',
- subtitle:
- 'Integrate CloudWatch to see backend logs and errors alongside session replay.',
- slug: 'cloudwatch',
- icon: 'integrations/aws',
- component:
,
- },
- {
- title: 'Newrelic',
- subtitle:
- 'Integrate NewRelic with session replays to seamlessly observe backend errors.',
- slug: 'newrelic',
- icon: 'integrations/newrelic',
- component:
,
+ 'Integrate Dynatrace with session replays to link backend logs with user sessions for faster issue resolution.',
+ slug: 'dynatrace',
+ icon: 'integrations/dynatrace',
+ useIcon: true,
+ component:
,
},
],
},
@@ -409,3 +377,56 @@ const integrations = [
],
},
];
+
+/**
+ *
+ * @deprecated
+ * */
+// {
+// title: 'Sumo Logic',
+// subtitle:
+// 'Integrate Sumo Logic with session replays to seamlessly observe backend errors.',
+// slug: 'sumologic',
+// icon: 'integrations/sumologic',
+// component:
,
+// },
+// {
+// title: 'Bugsnag',
+// subtitle:
+// 'Integrate Bugsnag to access the OpenReplay session linked to the JS exception within its interface.',
+// slug: 'bugsnag',
+// icon: 'integrations/bugsnag',
+// component:
,
+// },
+// {
+// title: 'Rollbar',
+// subtitle:
+// 'Integrate Rollbar with session replays to seamlessly observe backend errors.',
+// slug: 'rollbar',
+// icon: 'integrations/rollbar',
+// component:
,
+// },
+// {
+// title: 'Google Cloud',
+// subtitle:
+// 'Integrate Google Cloud to view backend logs and errors in conjunction with session replay',
+// slug: 'stackdriver',
+// icon: 'integrations/google-cloud',
+// component:
,
+// },
+// {
+// title: 'CloudWatch',
+// subtitle:
+// 'Integrate CloudWatch to see backend logs and errors alongside session replay.',
+// slug: 'cloudwatch',
+// icon: 'integrations/aws',
+// component:
,
+// },
+// {
+// title: 'Newrelic',
+// subtitle:
+// 'Integrate NewRelic with session replays to seamlessly observe backend errors.',
+// slug: 'newrelic',
+// icon: 'integrations/newrelic',
+// component:
,
+// },
diff --git a/frontend/app/components/Client/Integrations/JiraForm/JiraForm.js b/frontend/app/components/Client/Integrations/JiraForm/JiraForm.js
index c417f0626..dc8f3e49f 100644
--- a/frontend/app/components/Client/Integrations/JiraForm/JiraForm.js
+++ b/frontend/app/components/Client/Integrations/JiraForm/JiraForm.js
@@ -2,7 +2,6 @@ import React from 'react';
import IntegrationForm from '../IntegrationForm';
import DocLink from 'Shared/DocLink/DocLink';
import { useModal } from 'App/components/Modal';
-import { Icon } from 'UI';
import IntegrationModalCard from 'Components/Client/Integrations/IntegrationModalCard';
const JiraForm = (props) => {
diff --git a/frontend/app/components/Client/Integrations/NewrelicForm/NewrelicForm.js b/frontend/app/components/Client/Integrations/NewrelicForm/NewrelicForm.js
deleted file mode 100644
index 3d3d829b1..000000000
--- a/frontend/app/components/Client/Integrations/NewrelicForm/NewrelicForm.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react';
-import IntegrationForm from '../IntegrationForm';
-import DocLink from 'Shared/DocLink/DocLink';
-import IntegrationModalCard from 'Components/Client/Integrations/IntegrationModalCard';
-
-const NewrelicForm = (props) => (
-
-
-
-
How it works?
-
- - Create Query Key
- - Enter the details below
- - Propagate openReplaySessionToken
-
-
-
-
-
-);
-
-NewrelicForm.displayName = 'NewrelicForm';
-
-export default NewrelicForm;
diff --git a/frontend/app/components/Client/Integrations/NewrelicForm/index.js b/frontend/app/components/Client/Integrations/NewrelicForm/index.js
deleted file mode 100644
index 0d4052873..000000000
--- a/frontend/app/components/Client/Integrations/NewrelicForm/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './NewrelicForm';
diff --git a/frontend/app/components/Client/Integrations/RollbarForm.js b/frontend/app/components/Client/Integrations/RollbarForm.js
deleted file mode 100644
index 624ec9bf2..000000000
--- a/frontend/app/components/Client/Integrations/RollbarForm.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import IntegrationForm from './IntegrationForm';
-import DocLink from 'Shared/DocLink/DocLink';
-import IntegrationModalCard from 'Components/Client/Integrations/IntegrationModalCard';
-
-const RollbarForm = (props) => (
-
-
-
-
How it works?
-
- - Create Rollbar Access Tokens
- - Enter the token below
- - Propagate openReplaySessionToken
-
-
-
-
-
-);
-
-RollbarForm.displayName = 'RollbarForm';
-
-export default RollbarForm;
diff --git a/frontend/app/components/Client/Integrations/SentryForm.js b/frontend/app/components/Client/Integrations/SentryForm.js
deleted file mode 100644
index 506aec48c..000000000
--- a/frontend/app/components/Client/Integrations/SentryForm.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import IntegrationForm from './IntegrationForm';
-import DocLink from 'Shared/DocLink/DocLink';
-import IntegrationModalCard from 'Components/Client/Integrations/IntegrationModalCard';
-
-const SentryForm = (props) => (
-
-
-
-
How it works?
-
- - Generate Sentry Auth Token
- - Enter the token below
- - Propagate openReplaySessionToken
-
-
-
-
-
-);
-
-SentryForm.displayName = 'SentryForm';
-
-export default SentryForm;
diff --git a/frontend/app/components/Client/Integrations/StackdriverForm.js b/frontend/app/components/Client/Integrations/StackdriverForm.js
deleted file mode 100644
index 92935e67b..000000000
--- a/frontend/app/components/Client/Integrations/StackdriverForm.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import React from 'react';
-import IntegrationForm from './IntegrationForm';
-import DocLink from 'Shared/DocLink/DocLink';
-import IntegrationModalCard from 'Components/Client/Integrations/IntegrationModalCard';
-
-const StackdriverForm = (props) => (
-
-
-
-
How it works?
-
- - Create Google Cloud Service Account
- - Enter the details below
- - Propagate openReplaySessionToken
-
-
-
-
-
-);
-
-StackdriverForm.displayName = 'StackdriverForm';
-
-export default StackdriverForm;
diff --git a/frontend/app/components/Client/Integrations/SumoLogicForm/RegionDropdown.js b/frontend/app/components/Client/Integrations/SumoLogicForm/RegionDropdown.js
deleted file mode 100644
index 48fa3151d..000000000
--- a/frontend/app/components/Client/Integrations/SumoLogicForm/RegionDropdown.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from 'react';
-import { regionLabels as labels } from 'Types/integrations/sumoLogicConfig';
-import Select from 'Shared/Select';
-
-const options = Object.keys(labels).map(key => ({ text: labels[ key ], label: key }));
-
-const RegionDropdown = props => (
-
)}
{!fullView ? (
diff --git a/frontend/app/components/Session/Player/SharedComponents/BackendLogs/BackendLogsPanel.tsx b/frontend/app/components/Session/Player/SharedComponents/BackendLogs/BackendLogsPanel.tsx
new file mode 100644
index 000000000..a215a1cea
--- /dev/null
+++ b/frontend/app/components/Session/Player/SharedComponents/BackendLogs/BackendLogsPanel.tsx
@@ -0,0 +1,161 @@
+import { useQuery } from '@tanstack/react-query';
+import { Segmented } from 'antd';
+import React from 'react';
+import { VList, VListHandle } from 'virtua';
+import { processLog, UnifiedLog } from './utils';
+import { observer } from 'mobx-react-lite';
+import { useStore } from 'App/mstore';
+import {
+ ServiceName,
+ serviceNames,
+} from 'App/components/Client/Integrations/apiMethods';
+import BottomBlock from 'App/components/shared/DevTools/BottomBlock';
+import { capitalize } from 'App/utils';
+import { Icon, Input } from 'UI';
+import { client } from 'App/mstore';
+import { FailedFetch, LoadingFetch } from "./StatusMessages";
+import {
+ TableHeader,
+ LogRow
+} from './Table'
+
+async function fetchLogs(
+ tab: string,
+ projectId: string,
+ sessionId: string
+): Promise