mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-17 22:35:59 +08:00
feat: revamp integration flow (#4832)
* feat: revamp integration flow * feat: final design changes * feat: make test connection button grey
This commit is contained in:
parent
872ed9e963
commit
59c242961f
@ -13,12 +13,18 @@ interface IntegrationDetailContentProps {
|
|||||||
activeDetailTab: string;
|
activeDetailTab: string;
|
||||||
integrationData: IntegrationDetailedProps;
|
integrationData: IntegrationDetailedProps;
|
||||||
integrationId: string;
|
integrationId: string;
|
||||||
|
setActiveDetailTab: React.Dispatch<React.SetStateAction<string | null>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function IntegrationDetailContent(
|
function IntegrationDetailContent(
|
||||||
props: IntegrationDetailContentProps,
|
props: IntegrationDetailContentProps,
|
||||||
): JSX.Element {
|
): JSX.Element {
|
||||||
const { activeDetailTab, integrationData, integrationId } = props;
|
const {
|
||||||
|
activeDetailTab,
|
||||||
|
integrationData,
|
||||||
|
integrationId,
|
||||||
|
setActiveDetailTab,
|
||||||
|
} = props;
|
||||||
const items: TabsProps['items'] = [
|
const items: TabsProps['items'] = [
|
||||||
{
|
{
|
||||||
key: 'overview',
|
key: 'overview',
|
||||||
@ -78,7 +84,11 @@ function IntegrationDetailContent(
|
|||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<div className="integration-detail-container">
|
<div className="integration-detail-container">
|
||||||
<Tabs defaultActiveKey={activeDetailTab} items={items} />
|
<Tabs
|
||||||
|
activeKey={activeDetailTab}
|
||||||
|
items={items}
|
||||||
|
onChange={setActiveDetailTab}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ import './IntegrationDetailPage.styles.scss';
|
|||||||
|
|
||||||
import { Button, Modal, Tooltip, Typography } from 'antd';
|
import { Button, Modal, Tooltip, Typography } from 'antd';
|
||||||
import installIntegration from 'api/Integrations/installIntegration';
|
import installIntegration from 'api/Integrations/installIntegration';
|
||||||
|
import ConfigureIcon from 'assets/Integrations/ConfigureIcon';
|
||||||
|
import cx from 'classnames';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import useAnalytics from 'hooks/analytics/useAnalytics';
|
import useAnalytics from 'hooks/analytics/useAnalytics';
|
||||||
@ -23,6 +25,7 @@ interface IntegrationDetailHeaderProps {
|
|||||||
refetchIntegrationDetails: () => void;
|
refetchIntegrationDetails: () => void;
|
||||||
connectionState: ConnectionStates;
|
connectionState: ConnectionStates;
|
||||||
connectionData: IntegrationConnectionStatus;
|
connectionData: IntegrationConnectionStatus;
|
||||||
|
setActiveDetailTab: React.Dispatch<React.SetStateAction<string | null>>;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
function IntegrationDetailHeader(
|
function IntegrationDetailHeader(
|
||||||
@ -36,6 +39,7 @@ function IntegrationDetailHeader(
|
|||||||
connectionState,
|
connectionState,
|
||||||
connectionData,
|
connectionData,
|
||||||
refetchIntegrationDetails,
|
refetchIntegrationDetails,
|
||||||
|
setActiveDetailTab,
|
||||||
} = props;
|
} = props;
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
@ -106,6 +110,12 @@ function IntegrationDetailHeader(
|
|||||||
last_received_from: connectionData.metrics.last_received_from,
|
last_received_from: connectionData.metrics.last_received_from,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const isConnectionStatePending =
|
||||||
|
connectionState === ConnectionStates.NotInstalled ||
|
||||||
|
connectionState === ConnectionStates.TestingConnection;
|
||||||
|
|
||||||
|
const isConnectionStateNotInstalled =
|
||||||
|
connectionState === ConnectionStates.NotInstalled;
|
||||||
return (
|
return (
|
||||||
<div className="integration-connection-header">
|
<div className="integration-connection-header">
|
||||||
<div className="integration-detail-header" key={id}>
|
<div className="integration-detail-header" key={id}>
|
||||||
@ -119,7 +129,10 @@ function IntegrationDetailHeader(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
className="configure-btn"
|
className={cx(
|
||||||
|
'configure-btn',
|
||||||
|
!isConnectionStateNotInstalled && 'test-connection',
|
||||||
|
)}
|
||||||
icon={<ArrowLeftRight size={14} />}
|
icon={<ArrowLeftRight size={14} />}
|
||||||
disabled={isInstallLoading}
|
disabled={isInstallLoading}
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
@ -127,7 +140,6 @@ function IntegrationDetailHeader(
|
|||||||
trackEvent(INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_CONNECT, {
|
trackEvent(INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_CONNECT, {
|
||||||
integration: id,
|
integration: id,
|
||||||
});
|
});
|
||||||
mutate({ integration_id: id, config: {} });
|
|
||||||
} else {
|
} else {
|
||||||
trackEvent(
|
trackEvent(
|
||||||
INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_TEST_CONNECTION,
|
INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_TEST_CONNECTION,
|
||||||
@ -136,13 +148,11 @@ function IntegrationDetailHeader(
|
|||||||
connectionStatus: connectionState,
|
connectionStatus: connectionState,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
showModal();
|
|
||||||
}
|
}
|
||||||
|
showModal();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{connectionState === ConnectionStates.NotInstalled
|
{isConnectionStateNotInstalled ? `Connect ${title}` : `Test Connection`}
|
||||||
? `Connect ${title}`
|
|
||||||
: `Test Connection`}
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -153,15 +163,67 @@ function IntegrationDetailHeader(
|
|||||||
<Modal
|
<Modal
|
||||||
className="test-connection-modal"
|
className="test-connection-modal"
|
||||||
open={isModalOpen}
|
open={isModalOpen}
|
||||||
title="Test Connection"
|
title={
|
||||||
onOk={handleOk}
|
isConnectionStateNotInstalled
|
||||||
|
? `Connect ${title}`
|
||||||
|
: `Test ${title} Connection`
|
||||||
|
}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
okText="I understand"
|
footer={
|
||||||
okButtonProps={{ className: 'understandBtn', icon: <Check size={14} /> }}
|
<div
|
||||||
cancelButtonProps={{ style: { display: 'none' } }}
|
className={cx(
|
||||||
|
'connection-footer',
|
||||||
|
!isConnectionStatePending && 'not-pending',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={
|
||||||
|
isConnectionStateNotInstalled ? <ConfigureIcon /> : <Check size={14} />
|
||||||
|
}
|
||||||
|
onClick={(): void => {
|
||||||
|
if (isConnectionStateNotInstalled) {
|
||||||
|
setActiveDetailTab('configuration');
|
||||||
|
}
|
||||||
|
handleOk();
|
||||||
|
}}
|
||||||
|
className="understandBtn"
|
||||||
|
>
|
||||||
|
{isConnectionStatePending
|
||||||
|
? isConnectionStateNotInstalled
|
||||||
|
? 'Show Configuration Steps'
|
||||||
|
: 'I have already configured'
|
||||||
|
: 'I understand'}
|
||||||
|
</Button>
|
||||||
|
{isConnectionStatePending && (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={
|
||||||
|
isConnectionStateNotInstalled ? <Check size={14} /> : <ConfigureIcon />
|
||||||
|
}
|
||||||
|
onClick={(): void => {
|
||||||
|
if (isConnectionStateNotInstalled) {
|
||||||
|
mutate({ integration_id: id, config: {} });
|
||||||
|
} else {
|
||||||
|
setActiveDetailTab('configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOk();
|
||||||
|
}}
|
||||||
|
className="configureBtn"
|
||||||
|
>
|
||||||
|
{isConnectionStateNotInstalled
|
||||||
|
? 'I have already configured'
|
||||||
|
: 'Show Configuration Steps'}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className="connection-content">
|
<div className="connection-content">
|
||||||
<TestConnection connectionState={connectionState} />
|
{!isConnectionStateNotInstalled && (
|
||||||
|
<TestConnection connectionState={connectionState} />
|
||||||
|
)}
|
||||||
{connectionState === ConnectionStates.Connected ||
|
{connectionState === ConnectionStates.Connected ||
|
||||||
connectionState === ConnectionStates.NoDataSinceLong ? (
|
connectionState === ConnectionStates.NoDataSinceLong ? (
|
||||||
<>
|
<>
|
||||||
@ -210,12 +272,26 @@ function IntegrationDetailHeader(
|
|||||||
) : connectionState === ConnectionStates.TestingConnection ? (
|
) : connectionState === ConnectionStates.TestingConnection ? (
|
||||||
<div className="data-test-connection">
|
<div className="data-test-connection">
|
||||||
<div className="last-data">
|
<div className="last-data">
|
||||||
After adding the {title} integration, you need to manually configure
|
We have not received data from your {title} Instance yet. You need to
|
||||||
your Redis data source to start sending data to SigNoz.
|
manually configure your {title} instance to start sending data to
|
||||||
|
SigNoz.
|
||||||
</div>
|
</div>
|
||||||
<div className="last-data">
|
<div className="last-data">
|
||||||
The status bar above would turn green if we are successfully receiving
|
If you have already configured your resources to send data, sit tight
|
||||||
the data.
|
and wait for the data to flow in, Or else, see the steps to configure
|
||||||
|
your resources to start sending data.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : isConnectionStateNotInstalled ? (
|
||||||
|
<div className="data-test-connection">
|
||||||
|
<div className="last-data">
|
||||||
|
You would need to manually configure your {title} instance to start
|
||||||
|
sending data to SigNoz.
|
||||||
|
</div>
|
||||||
|
<div className="last-data">
|
||||||
|
If you have already configured your resources to send data, sit tight
|
||||||
|
and wait for the data to flow in, Or else, see the steps to configure
|
||||||
|
your resources to start sending data.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -160,6 +160,14 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 10px; /* 83.333% */
|
line-height: 10px; /* 83.333% */
|
||||||
letter-spacing: 0.12px;
|
letter-spacing: 0.12px;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
&.test-connection {
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid var(--bg-slate-400);
|
||||||
|
background: var(--bg-ink-300);
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,25 +388,56 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
|
|
||||||
.understandBtn {
|
.connection-footer {
|
||||||
border-radius: 2px;
|
|
||||||
border: 1px solid var(--bg-slate-400);
|
|
||||||
background: var(--bg-ink-300);
|
|
||||||
box-shadow: none;
|
|
||||||
color: var(--bg-vanilla-400);
|
|
||||||
font-family: Inter;
|
|
||||||
font-size: 12px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 10px; /* 83.333% */
|
|
||||||
letter-spacing: 0.12px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
width: 100%;
|
||||||
align-items: center;
|
.understandBtn {
|
||||||
width: 131px;
|
width: 50%;
|
||||||
height: 30px;
|
border-radius: 2px;
|
||||||
padding: 6px;
|
border: 1px solid var(--bg-slate-400);
|
||||||
flex-shrink: 0;
|
background: var(--bg-ink-300);
|
||||||
|
box-shadow: none;
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 10px; /* 83.333% */
|
||||||
|
letter-spacing: 0.12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 34px;
|
||||||
|
padding: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.configureBtn {
|
||||||
|
width: 50%;
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 10px; /* 83.333% */
|
||||||
|
letter-spacing: 0.12px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: var(--bg-robin-500);
|
||||||
|
display: flex;
|
||||||
|
height: 34px;
|
||||||
|
padding: 6px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex: 1 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.not-pending {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
|
||||||
|
.understandBtn {
|
||||||
|
width: 131px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -544,6 +583,13 @@
|
|||||||
color: var(--bg-slate-200);
|
color: var(--bg-slate-200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.configure-btn {
|
||||||
|
&.test-connection {
|
||||||
|
border: 1px solid rgba(53, 59, 76, 0.2);
|
||||||
|
background: var(--bg-vanilla-200);
|
||||||
|
color: var(--bg-slate-200);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.testingConnection {
|
.testingConnection {
|
||||||
|
@ -22,10 +22,16 @@ interface IntegrationDetailPageProps {
|
|||||||
selectedIntegration: string;
|
selectedIntegration: string;
|
||||||
setSelectedIntegration: (id: string | null) => void;
|
setSelectedIntegration: (id: string | null) => void;
|
||||||
activeDetailTab: string;
|
activeDetailTab: string;
|
||||||
|
setActiveDetailTab: React.Dispatch<React.SetStateAction<string | null>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function IntegrationDetailPage(props: IntegrationDetailPageProps): JSX.Element {
|
function IntegrationDetailPage(props: IntegrationDetailPageProps): JSX.Element {
|
||||||
const { selectedIntegration, setSelectedIntegration, activeDetailTab } = props;
|
const {
|
||||||
|
selectedIntegration,
|
||||||
|
setSelectedIntegration,
|
||||||
|
activeDetailTab,
|
||||||
|
setActiveDetailTab,
|
||||||
|
} = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@ -119,11 +125,13 @@ function IntegrationDetailPage(props: IntegrationDetailPageProps): JSX.Element {
|
|||||||
metrics: null,
|
metrics: null,
|
||||||
})}
|
})}
|
||||||
refetchIntegrationDetails={refetch}
|
refetchIntegrationDetails={refetch}
|
||||||
|
setActiveDetailTab={setActiveDetailTab}
|
||||||
/>
|
/>
|
||||||
<IntegrationDetailContent
|
<IntegrationDetailContent
|
||||||
activeDetailTab={activeDetailTab}
|
activeDetailTab={activeDetailTab}
|
||||||
integrationData={integrationData}
|
integrationData={integrationData}
|
||||||
integrationId={selectedIntegration}
|
integrationId={selectedIntegration}
|
||||||
|
setActiveDetailTab={setActiveDetailTab}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{connectionStatus !== ConnectionStates.NotInstalled && (
|
{connectionStatus !== ConnectionStates.NotInstalled && (
|
||||||
|
@ -55,6 +55,7 @@ function Integrations(): JSX.Element {
|
|||||||
selectedIntegration={selectedIntegration}
|
selectedIntegration={selectedIntegration}
|
||||||
setSelectedIntegration={setSelectedIntegration}
|
setSelectedIntegration={setSelectedIntegration}
|
||||||
activeDetailTab={activeDetailTab}
|
activeDetailTab={activeDetailTab}
|
||||||
|
setActiveDetailTab={setActiveDetailTab}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user