mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-08 14:19:01 +08:00
feat: add integrations to the side-nav for cloud users (#4756)
* feat: add integrations to the side-nav for cloud users * feat: change the route from integrations/installed to /integrations * feat: light mode table color * feat: increase the width of the integrations panel by 25 percent * feat: added telemetry constants and page view * feat: added telemetry events for integrations * feat: address review comments
This commit is contained in:
parent
39e0ef68ca
commit
00d74bfebb
@ -48,5 +48,5 @@
|
||||
"TRACES_SAVE_VIEWS": "SigNoz | Traces Saved Views",
|
||||
"DEFAULT": "Open source Observability Platform | SigNoz",
|
||||
"SHORTCUTS": "SigNoz | Shortcuts",
|
||||
"INTEGRATIONS_INSTALLED": "SigNoz | Integrations"
|
||||
"INTEGRATIONS": "SigNoz | Integrations"
|
||||
}
|
||||
|
@ -197,11 +197,3 @@ export const InstalledIntegrations = Loadable(
|
||||
/* webpackChunkName: "InstalledIntegrations" */ 'pages/IntegrationsModulePage'
|
||||
),
|
||||
);
|
||||
|
||||
export const IntegrationsMarketPlace = Loadable(
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "IntegrationsMarketPlace" */ 'pages/IntegrationsModulePage'
|
||||
),
|
||||
);
|
||||
|
@ -15,7 +15,6 @@ import {
|
||||
ErrorDetails,
|
||||
IngestionSettings,
|
||||
InstalledIntegrations,
|
||||
IntegrationsMarketPlace,
|
||||
LicensePage,
|
||||
ListAllALertsPage,
|
||||
LiveLogs,
|
||||
@ -338,18 +337,11 @@ const routes: AppRoutes[] = [
|
||||
key: 'SHORTCUTS',
|
||||
},
|
||||
{
|
||||
path: ROUTES.INTEGRATIONS_INSTALLED,
|
||||
path: ROUTES.INTEGRATIONS,
|
||||
exact: true,
|
||||
component: InstalledIntegrations,
|
||||
isPrivate: true,
|
||||
key: 'INTEGRATIONS_INSTALLED',
|
||||
},
|
||||
{
|
||||
path: ROUTES.INTEGRATIONS_MARKETPLACE,
|
||||
exact: true,
|
||||
component: IntegrationsMarketPlace,
|
||||
isPrivate: true,
|
||||
key: 'INTEGRATIONS_MARKETPLACE',
|
||||
key: 'INTEGRATIONS',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -51,9 +51,7 @@ const ROUTES = {
|
||||
TRACES_SAVE_VIEWS: '/traces/saved-views',
|
||||
WORKSPACE_LOCKED: '/workspace-locked',
|
||||
SHORTCUTS: '/shortcuts',
|
||||
INTEGRATIONS_BASE: '/integrations',
|
||||
INTEGRATIONS_INSTALLED: '/integrations/installed',
|
||||
INTEGRATIONS_MARKETPLACE: '/integrations/marketplace',
|
||||
INTEGRATIONS: '/integrations',
|
||||
} as const;
|
||||
|
||||
export default ROUTES;
|
||||
|
@ -271,6 +271,17 @@ function SideNav({
|
||||
}
|
||||
}, [isCloudUserVal, isEnterprise, isFetching]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCloudUserVal) {
|
||||
let updatedMenuItems = [...menuItems];
|
||||
updatedMenuItems = updatedMenuItems.filter(
|
||||
(item) => item.key !== ROUTES.INTEGRATIONS,
|
||||
);
|
||||
setMenuItems(updatedMenuItems);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const [isCurrentOrgSettings] = useComponentPermission(
|
||||
['current_org_settings'],
|
||||
role,
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
ScrollText,
|
||||
Settings,
|
||||
Slack,
|
||||
Unplug,
|
||||
// Unplug,
|
||||
UserPlus,
|
||||
} from 'lucide-react';
|
||||
@ -90,11 +91,11 @@ const menuItems: SidebarItem[] = [
|
||||
label: 'Alerts',
|
||||
icon: <BellDot size={16} />,
|
||||
},
|
||||
// {
|
||||
// key: ROUTES.INTEGRATIONS_INSTALLED,
|
||||
// label: 'Integrations',
|
||||
// icon: <Unplug size={16} />,
|
||||
// },
|
||||
{
|
||||
key: ROUTES.INTEGRATIONS,
|
||||
label: 'Integrations',
|
||||
icon: <Unplug size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.ALL_ERROR,
|
||||
label: 'Exceptions',
|
||||
@ -127,7 +128,6 @@ export const NEW_ROUTES_MENU_ITEM_KEY_MAP: Record<string, string> = {
|
||||
[ROUTES.TRACES_EXPLORER]: ROUTES.TRACE,
|
||||
[ROUTES.TRACE_EXPLORER]: ROUTES.TRACE,
|
||||
[ROUTES.LOGS_BASE]: ROUTES.LOGS_EXPLORER,
|
||||
[ROUTES.INTEGRATIONS_BASE]: ROUTES.INTEGRATIONS_INSTALLED,
|
||||
};
|
||||
|
||||
export default menuItems;
|
||||
|
@ -199,9 +199,7 @@ export const routesToSkip = [
|
||||
ROUTES.TRACES_EXPLORER,
|
||||
ROUTES.TRACES_SAVE_VIEWS,
|
||||
ROUTES.SHORTCUTS,
|
||||
ROUTES.INTEGRATIONS_BASE,
|
||||
ROUTES.INTEGRATIONS_INSTALLED,
|
||||
ROUTES.INTEGRATIONS_MARKETPLACE,
|
||||
ROUTES.INTEGRATIONS,
|
||||
];
|
||||
|
||||
export const routesToDisable = [ROUTES.LOGS_EXPLORER, ROUTES.LIVE_LOGS];
|
||||
|
@ -12,12 +12,13 @@ import Overview from './IntegrationDetailContentTabs/Overview';
|
||||
interface IntegrationDetailContentProps {
|
||||
activeDetailTab: string;
|
||||
integrationData: IntegrationDetailedProps;
|
||||
integrationId: string;
|
||||
}
|
||||
|
||||
function IntegrationDetailContent(
|
||||
props: IntegrationDetailContentProps,
|
||||
): JSX.Element {
|
||||
const { activeDetailTab, integrationData } = props;
|
||||
const { activeDetailTab, integrationData, integrationId } = props;
|
||||
const items: TabsProps['items'] = [
|
||||
{
|
||||
key: 'overview',
|
||||
@ -49,7 +50,12 @@ function IntegrationDetailContent(
|
||||
<Typography.Text className="typography">Configure</Typography.Text>
|
||||
</Button>
|
||||
),
|
||||
children: <Configure configuration={integrationData.configuration} />,
|
||||
children: (
|
||||
<Configure
|
||||
configuration={integrationData.configuration}
|
||||
integrationId={integrationId}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'dataCollected',
|
||||
|
@ -3,20 +3,36 @@ import './IntegrationDetailContentTabs.styles.scss';
|
||||
import { Button, Typography } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer';
|
||||
import { useState } from 'react';
|
||||
import useAnalytics from 'hooks/analytics/useAnalytics';
|
||||
import { INTEGRATION_TELEMETRY_EVENTS } from 'pages/Integrations/utils';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
interface ConfigurationProps {
|
||||
configuration: Array<{ title: string; instructions: string }>;
|
||||
integrationId: string;
|
||||
}
|
||||
|
||||
function Configure(props: ConfigurationProps): JSX.Element {
|
||||
// TODO Mardown renderer support once instructions are ready
|
||||
const { configuration } = props;
|
||||
const { configuration, integrationId } = props;
|
||||
const [selectedConfigStep, setSelectedConfigStep] = useState(0);
|
||||
|
||||
const handleMenuClick = (index: number): void => {
|
||||
setSelectedConfigStep(index);
|
||||
};
|
||||
|
||||
const { trackEvent } = useAnalytics();
|
||||
|
||||
useEffect(() => {
|
||||
trackEvent(
|
||||
INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_CONFIGURE_INSTRUCTION,
|
||||
{
|
||||
integration: integrationId,
|
||||
},
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="integration-detail-configure">
|
||||
<div className="configure-menu">
|
||||
|
@ -260,7 +260,7 @@
|
||||
|
||||
.logs-section {
|
||||
.table-row-dark {
|
||||
background: rgba(255, 255, 255, 0.01);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.logs-section-table {
|
||||
@ -271,7 +271,7 @@
|
||||
|
||||
.metrics-section {
|
||||
.table-row-dark {
|
||||
background: rgba(255, 255, 255, 0.01);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.metrics-section-table {
|
||||
|
@ -5,12 +5,14 @@ import { Button, Modal, Tooltip, Typography } from 'antd';
|
||||
import installIntegration from 'api/Integrations/installIntegration';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import dayjs from 'dayjs';
|
||||
import useAnalytics from 'hooks/analytics/useAnalytics';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { ArrowLeftRight, Check } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useMutation } from 'react-query';
|
||||
import { IntegrationConnectionStatus } from 'types/api/integrations/types';
|
||||
|
||||
import { INTEGRATION_TELEMETRY_EVENTS } from '../utils';
|
||||
import TestConnection, { ConnectionStates } from './TestConnection';
|
||||
|
||||
interface IntegrationDetailHeaderProps {
|
||||
@ -37,6 +39,8 @@ function IntegrationDetailHeader(
|
||||
} = props;
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const { trackEvent } = useAnalytics();
|
||||
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
const showModal = (): void => {
|
||||
@ -120,8 +124,18 @@ function IntegrationDetailHeader(
|
||||
disabled={isInstallLoading}
|
||||
onClick={(): void => {
|
||||
if (connectionState === ConnectionStates.NotInstalled) {
|
||||
trackEvent(INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_CONNECT, {
|
||||
integration: id,
|
||||
});
|
||||
mutate({ integration_id: id, config: {} });
|
||||
} else {
|
||||
trackEvent(
|
||||
INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_TEST_CONNECTION,
|
||||
{
|
||||
integration: id,
|
||||
connectionStatus: connectionState,
|
||||
},
|
||||
);
|
||||
showModal();
|
||||
}
|
||||
}}
|
||||
|
@ -123,6 +123,7 @@ function IntegrationDetailPage(props: IntegrationDetailPageProps): JSX.Element {
|
||||
<IntegrationDetailContent
|
||||
activeDetailTab={activeDetailTab}
|
||||
integrationData={integrationData}
|
||||
integrationId={selectedIntegration}
|
||||
/>
|
||||
|
||||
{connectionStatus !== ConnectionStates.NotInstalled && (
|
||||
@ -130,6 +131,7 @@ function IntegrationDetailPage(props: IntegrationDetailPageProps): JSX.Element {
|
||||
integrationTitle={defaultTo(integrationData?.title, '')}
|
||||
integrationId={selectedIntegration}
|
||||
refetchIntegrationDetails={refetch}
|
||||
connectionStatus={connectionStatus}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@ -3,23 +3,35 @@ import './IntegrationDetailPage.styles.scss';
|
||||
import { Button, Modal, Typography } from 'antd';
|
||||
import unInstallIntegration from 'api/Integrations/uninstallIntegration';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import useAnalytics from 'hooks/analytics/useAnalytics';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { X } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useMutation } from 'react-query';
|
||||
|
||||
import { INTEGRATION_TELEMETRY_EVENTS } from '../utils';
|
||||
import { ConnectionStates } from './TestConnection';
|
||||
|
||||
interface IntergrationsUninstallBarProps {
|
||||
integrationTitle: string;
|
||||
integrationId: string;
|
||||
refetchIntegrationDetails: () => void;
|
||||
connectionStatus: ConnectionStates;
|
||||
}
|
||||
function IntergrationsUninstallBar(
|
||||
props: IntergrationsUninstallBarProps,
|
||||
): JSX.Element {
|
||||
const { integrationTitle, integrationId, refetchIntegrationDetails } = props;
|
||||
const {
|
||||
integrationTitle,
|
||||
integrationId,
|
||||
refetchIntegrationDetails,
|
||||
connectionStatus,
|
||||
} = props;
|
||||
const { notifications } = useNotifications();
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const { trackEvent } = useAnalytics();
|
||||
|
||||
const {
|
||||
mutate: uninstallIntegration,
|
||||
isLoading: isUninstallLoading,
|
||||
@ -40,6 +52,13 @@ function IntergrationsUninstallBar(
|
||||
};
|
||||
|
||||
const handleOk = (): void => {
|
||||
trackEvent(
|
||||
INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_REMOVE_INTEGRATION,
|
||||
{
|
||||
integration: integrationId,
|
||||
integrationStatus: connectionStatus,
|
||||
},
|
||||
);
|
||||
uninstallIntegration({
|
||||
integration_id: integrationId,
|
||||
});
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
.integrations-content {
|
||||
width: calc(100% - 30px);
|
||||
max-width: 736px;
|
||||
max-width: 920px;
|
||||
|
||||
.integrations-header {
|
||||
.title {
|
||||
|
@ -1,18 +1,22 @@
|
||||
import './Integrations.styles.scss';
|
||||
|
||||
import useAnalytics from 'hooks/analytics/useAnalytics';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
|
||||
import Header from './Header';
|
||||
import IntegrationDetailPage from './IntegrationDetailPage/IntegrationDetailPage';
|
||||
import IntegrationsList from './IntegrationsList';
|
||||
import { INTEGRATION_TELEMETRY_EVENTS } from './utils';
|
||||
|
||||
function Integrations(): JSX.Element {
|
||||
const urlQuery = useUrlQuery();
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
|
||||
const { trackPageView, trackEvent } = useAnalytics();
|
||||
|
||||
const selectedIntegration = useMemo(() => urlQuery.get('integration'), [
|
||||
urlQuery,
|
||||
]);
|
||||
@ -20,6 +24,9 @@ function Integrations(): JSX.Element {
|
||||
const setSelectedIntegration = useCallback(
|
||||
(integration: string | null) => {
|
||||
if (integration) {
|
||||
trackEvent(INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_ITEM_LIST_CLICKED, {
|
||||
integration,
|
||||
});
|
||||
urlQuery.set('integration', integration);
|
||||
} else {
|
||||
urlQuery.set('integration', '');
|
||||
@ -27,13 +34,18 @@ function Integrations(): JSX.Element {
|
||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
||||
history.push(generatedUrl);
|
||||
},
|
||||
[history, location.pathname, urlQuery],
|
||||
[history, location.pathname, trackEvent, urlQuery],
|
||||
);
|
||||
|
||||
const [activeDetailTab, setActiveDetailTab] = useState<string | null>(
|
||||
'overview',
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
trackPageView(location.pathname);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState<string>('');
|
||||
return (
|
||||
<div className="integrations-container">
|
||||
|
@ -7,3 +7,15 @@ export const handleContactSupport = (isCloudUser: boolean): void => {
|
||||
window.open('https://signoz.io/slack', '_blank');
|
||||
}
|
||||
};
|
||||
|
||||
export const INTEGRATION_TELEMETRY_EVENTS = {
|
||||
INTEGRATIONS_ITEM_LIST_CLICKED: 'Integrations Page: Clicked an integration',
|
||||
INTEGRATIONS_DETAIL_CONNECT:
|
||||
'Integrations Detail Page: Clicked connect integration button',
|
||||
INTEGRATIONS_DETAIL_TEST_CONNECTION:
|
||||
'Integrations Detail Page: Clicked test Connection button for integration',
|
||||
INTEGRATIONS_DETAIL_REMOVE_INTEGRATION:
|
||||
'Integrations Detail Page: Clicked remove Integration button for integration',
|
||||
INTEGRATIONS_DETAIL_CONFIGURE_INSTRUCTION:
|
||||
'Integrations Detail Page: Navigated to configure an integration',
|
||||
};
|
||||
|
@ -10,6 +10,6 @@ export const installedIntegrations: TabRoutes = {
|
||||
<Compass size={16} /> Integrations
|
||||
</div>
|
||||
),
|
||||
route: ROUTES.INTEGRATIONS_INSTALLED,
|
||||
key: ROUTES.INTEGRATIONS_INSTALLED,
|
||||
route: ROUTES.INTEGRATIONS,
|
||||
key: ROUTES.INTEGRATIONS,
|
||||
};
|
||||
|
@ -96,7 +96,5 @@ export const routePermission: Record<keyof typeof ROUTES, ROLES[]> = {
|
||||
LOGS_BASE: [],
|
||||
OLD_LOGS_EXPLORER: [],
|
||||
SHORTCUTS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
INTEGRATIONS_BASE: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
INTEGRATIONS_INSTALLED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
INTEGRATIONS_MARKETPLACE: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
INTEGRATIONS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user