@@ -15,10 +23,9 @@ function HeroSection(): JSX.Element {
AWS Web Services
- Monitor your AWS infrastructure with SigNoz. Get metrics and logs from your
- AWS services.
+ One-click setup for AWS monitoring with SigNoz
-
+
);
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/AccountActions.style.scss b/frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountActions.style.scss
similarity index 63%
rename from frontend/src/container/CloudIntegrationPage/HeroSection/AccountActions.style.scss
rename to frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountActions.style.scss
index 2fc33f70166..b1f36654ced 100644
--- a/frontend/src/container/CloudIntegrationPage/HeroSection/AccountActions.style.scss
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountActions.style.scss
@@ -77,3 +77,51 @@
}
}
}
+.lightMode {
+ .hero-section__action-button {
+ &.primary {
+ background: var(--bg-robin-500);
+ color: var(--bg-vanilla-100);
+ }
+
+ &.secondary {
+ border-color: var(--bg-vanilla-300);
+ color: var(--bg-ink-400);
+ background: var(--bg-vanilla-100);
+
+ &:hover {
+ border-color: var(--bg-vanilla-400);
+ color: var(--bg-ink-500);
+ }
+ }
+ }
+
+ .cloud-account-selector {
+ background: var(--bg-vanilla-100);
+ .ant-select-selector {
+ background: var(--bg-vanilla-100) !important;
+ border-color: var(--bg-vanilla-400) !important;
+ }
+ .ant-select-item-option-active {
+ background: var(--bg-vanilla-400) !important;
+ }
+
+ .ant-select-selection-item {
+ color: var(--bg-ink-400);
+ }
+
+ &:hover {
+ .ant-select-selector {
+ border-color: var(--bg-vanilla-400) !important;
+ }
+ }
+ }
+
+ .account-option-item {
+ color: var(--bg-ink-400);
+
+ &__selected {
+ background: var(--bg-robin-500);
+ }
+ }
+}
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountActions.tsx b/frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountActions.tsx
new file mode 100644
index 00000000000..fd5e246e7d8
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountActions.tsx
@@ -0,0 +1,159 @@
+import './AccountActions.style.scss';
+
+import { Color } from '@signozhq/design-tokens';
+import { Button, Select } from 'antd';
+import { SelectProps } from 'antd/lib';
+import { useAwsAccounts } from 'hooks/integrations/aws/useAwsAccounts';
+import useUrlQuery from 'hooks/useUrlQuery';
+import { Check, ChevronDown } from 'lucide-react';
+import { useEffect, useMemo, useState } from 'react';
+import { useNavigate } from 'react-router-dom-v5-compat';
+
+import { CloudAccount } from '../../ServicesSection/types';
+import AccountSettingsModal from './AccountSettingsModal';
+import CloudAccountSetupModal from './CloudAccountSetupModal';
+
+interface AccountOptionItemProps {
+ label: React.ReactNode;
+ isSelected: boolean;
+}
+
+function AccountOptionItem({
+ label,
+ isSelected,
+}: AccountOptionItemProps): JSX.Element {
+ return (
+
+ {label}
+ {isSelected && (
+
+
+
+ )}
+
+ );
+}
+
+function renderOption(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ option: any,
+ activeAccountId: string | undefined,
+): JSX.Element {
+ return (
+
+ );
+}
+
+const getAccountById = (
+ accounts: CloudAccount[],
+ accountId: string,
+): CloudAccount | null =>
+ accounts.find((account) => account.cloud_account_id === accountId) || null;
+
+function AccountActions(): JSX.Element {
+ const urlQuery = useUrlQuery();
+ const navigate = useNavigate();
+ const { data: accounts } = useAwsAccounts();
+
+ const initialAccount = useMemo(
+ () =>
+ accounts?.length
+ ? getAccountById(accounts, urlQuery.get('accountId') || '') || accounts[0]
+ : null,
+ [accounts, urlQuery],
+ );
+
+ const [activeAccount, setActiveAccount] = useState
(
+ initialAccount,
+ );
+
+ // Update state when initial value changes
+ useEffect(() => {
+ if (initialAccount !== null) {
+ setActiveAccount(initialAccount);
+ urlQuery.set('accountId', initialAccount.cloud_account_id);
+ navigate({ search: urlQuery.toString() });
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [initialAccount]);
+
+ const [isIntegrationModalOpen, setIsIntegrationModalOpen] = useState(false);
+ const [isAccountSettingsModalOpen, setIsAccountSettingsModalOpen] = useState(
+ false,
+ );
+
+ const selectOptions: SelectProps['options'] = useMemo(
+ () =>
+ accounts?.length
+ ? accounts.map((account) => ({
+ value: account.cloud_account_id,
+ label: account.cloud_account_id,
+ }))
+ : [],
+ [accounts],
+ );
+
+ return (
+
+ {accounts?.length ? (
+
+
}
+ optionRender={(option): JSX.Element =>
+ renderOption(option, activeAccount?.cloud_account_id)
+ }
+ onChange={(value): void => {
+ setActiveAccount(getAccountById(accounts, value));
+ urlQuery.set('accountId', value);
+ navigate({ search: urlQuery.toString() });
+ }}
+ />
+
+ setIsIntegrationModalOpen(true)}
+ >
+ Add New AWS Account
+
+ setIsAccountSettingsModalOpen(true)}
+ >
+ Account Settings
+
+
+
+ ) : (
+
setIsIntegrationModalOpen(true)}
+ >
+ Integrate Now
+
+ )}
+
+
setIsIntegrationModalOpen(false)}
+ />
+
+ setIsAccountSettingsModalOpen(false)}
+ account={activeAccount as CloudAccount}
+ setActiveAccount={setActiveAccount}
+ />
+
+ );
+}
+
+export default AccountActions;
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountSettingsModal.style.scss b/frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountSettingsModal.style.scss
new file mode 100644
index 00000000000..f2d1fd66fdf
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountSettingsModal.style.scss
@@ -0,0 +1,189 @@
+.account-settings-modal {
+ &__title-account-id {
+ color: var(--bg-vanilla-100);
+ font-family: 'Geist Mono';
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 20px;
+ }
+
+ &__body {
+ display: flex;
+ flex-direction: column;
+ gap: 17px;
+ border-radius: 3px;
+ border: 1px solid var(--bg-slate-500);
+ padding: 14px;
+ &-account-info {
+ &-connected-account-details {
+ &-title {
+ color: var(--bg-vanilla-100);
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px; /* 142.857% */
+ letter-spacing: -0.07px;
+ }
+ &-account-id {
+ color: var(--bg-vanilla-400);
+ font-size: 12px;
+ line-height: 18px;
+ letter-spacing: -0.06px;
+ &-account-id {
+ font-family: 'Geist Mono';
+ font-size: 12px;
+ font-weight: 700;
+ line-height: 18px;
+ letter-spacing: -0.06px;
+ }
+ }
+ }
+ }
+ &-regions-switch {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ &-title {
+ color: var(--bg-vanilla-100);
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ letter-spacing: -0.07px;
+ }
+ &-switch {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ &-label {
+ color: var(--bg-vanilla-400);
+ background-color: transparent;
+ border: none;
+ font-family: Inter;
+ font-size: 12px;
+ font-weight: 400;
+ line-height: 18px;
+ letter-spacing: -0.005em;
+ cursor: pointer;
+ }
+ }
+ }
+ &-regions-select {
+ margin-top: 8px;
+ }
+ }
+
+ &__footer {
+ &-close-button,
+ &-save-button {
+ color: var(--bg-vanilla-100);
+ font-family: Inter;
+ font-size: 12px;
+ font-weight: 500;
+ padding-left: 16px;
+ padding-right: 16px;
+ }
+ &-close-button {
+ border-radius: 2px;
+ background: var(--bg-slate-400);
+ border: none;
+ }
+ &-save-button {
+ &:disabled {
+ background: var(--bg-robin-500);
+ color: var(--bg-vanilla-100);
+ opacity: 0.6;
+ border: none;
+ }
+ border-radius: 2px;
+ margin: 0 !important;
+ }
+ }
+ .ant-modal-body {
+ padding-bottom: 0 !important;
+ }
+ .ant-modal-footer {
+ margin: 0;
+ padding: 24px 24px 12px;
+ }
+
+ .integration-detail-content {
+ margin: 0;
+ }
+}
+
+.lightMode {
+ .account-settings-modal {
+ &__title-account-id {
+ color: var(--bg-ink-500);
+ }
+
+ &__body {
+ border-color: var(--bg-vanilla-300);
+
+ &-account-info {
+ &-connected-account-details {
+ &-title {
+ color: var(--bg-ink-500);
+ }
+
+ &-account-id {
+ color: var(--bg-ink-400);
+
+ &-account-id {
+ color: var(--bg-ink-500);
+ }
+ }
+ }
+ }
+
+ &-regions-switch {
+ &-title {
+ color: var(--bg-ink-500);
+ }
+
+ &-switch {
+ &-label {
+ color: var(--bg-ink-400);
+
+ &:hover {
+ color: var(--bg-ink-500);
+ }
+ }
+ }
+ }
+ }
+
+ &__footer {
+ &-close-button,
+ &-save-button {
+ color: var(--bg-vanilla-100);
+ }
+
+ &-close-button {
+ background: var(--bg-vanilla-100);
+ border: 1px solid var(--bg-vanilla-300);
+ color: var(--bg-ink-400);
+
+ &:hover {
+ border-color: var(--bg-vanilla-400);
+ color: var(--bg-ink-500);
+ }
+ }
+
+ &-save-button {
+ // Keep primary button same as dark mode
+ background: var(--bg-robin-500);
+ color: var(--bg-vanilla-100);
+
+ &:disabled {
+ background: var(--bg-robin-500);
+ color: var(--bg-vanilla-100);
+ opacity: 0.6;
+ }
+
+ &:not(:disabled):hover {
+ background: var(--bg-robin-400);
+ }
+ }
+ }
+ }
+}
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountSettingsModal.tsx b/frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountSettingsModal.tsx
new file mode 100644
index 00000000000..209cb7f624d
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountSettingsModal.tsx
@@ -0,0 +1,182 @@
+import './AccountSettingsModal.style.scss';
+
+import { Form, Select, Switch } from 'antd';
+import SignozModal from 'components/SignozModal/SignozModal';
+import {
+ getRegionPreviewText,
+ useAccountSettingsModal,
+} from 'hooks/integrations/aws/useAccountSettingsModal';
+import IntergrationsUninstallBar from 'pages/Integrations/IntegrationDetailPage/IntegrationsUninstallBar';
+import { ConnectionStates } from 'pages/Integrations/IntegrationDetailPage/TestConnection';
+import { AWS_INTEGRATION } from 'pages/Integrations/IntegrationsList';
+import { Dispatch, SetStateAction, useCallback } from 'react';
+
+import { CloudAccount } from '../../ServicesSection/types';
+import { RegionSelector } from './RegionSelector';
+
+interface AccountSettingsModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ account: CloudAccount;
+ setActiveAccount: Dispatch>;
+}
+
+function AccountSettingsModal({
+ isOpen,
+ onClose,
+ account,
+ setActiveAccount,
+}: AccountSettingsModalProps): JSX.Element {
+ const {
+ form,
+ isLoading,
+ selectedRegions,
+ includeAllRegions,
+ isRegionSelectOpen,
+ isSaveDisabled,
+ setSelectedRegions,
+ setIncludeAllRegions,
+ setIsRegionSelectOpen,
+ handleIncludeAllRegionsChange,
+ handleSubmit,
+ handleClose,
+ } = useAccountSettingsModal({ onClose, account, setActiveAccount });
+
+ const renderRegionSelector = useCallback(() => {
+ if (isRegionSelectOpen) {
+ return (
+
+ );
+ }
+
+ return (
+ <>
+
+
+ handleIncludeAllRegionsChange(!includeAllRegions)}
+ >
+ Include all regions
+
+
+ setIsRegionSelectOpen(true)}
+ mode="multiple"
+ maxTagCount={3}
+ value={getRegionPreviewText(selectedRegions)}
+ open={false}
+ />
+ >
+ );
+ }, [
+ isRegionSelectOpen,
+ selectedRegions,
+ includeAllRegions,
+ handleIncludeAllRegionsChange,
+ setIsRegionSelectOpen,
+ setSelectedRegions,
+ setIncludeAllRegions,
+ ]);
+
+ const renderAccountDetails = useCallback(
+ () => (
+
+
+
+ Connected Account details
+
+
+ AWS Account:{' '}
+
+ {account?.id}
+
+
+
+
+ ),
+ [account?.id],
+ );
+
+ const modalTitle = (
+
+ Account settings for{' '}
+
+ {account?.id}
+
+
+ );
+
+ return (
+
+
+
+ );
+}
+
+export default AccountSettingsModal;
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/AlertMessage.tsx b/frontend/src/container/CloudIntegrationPage/HeroSection/components/AlertMessage.tsx
new file mode 100644
index 00000000000..c5f99298a2f
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/AlertMessage.tsx
@@ -0,0 +1,53 @@
+import { Color } from '@signozhq/design-tokens';
+import { Alert, Spin } from 'antd';
+import { LoaderCircle, TriangleAlert } from 'lucide-react';
+
+import { ModalStateEnum } from '../types';
+
+function AlertMessage({
+ modalState,
+}: {
+ modalState: ModalStateEnum;
+}): JSX.Element | null {
+ switch (modalState) {
+ case ModalStateEnum.WAITING:
+ return (
+
+
+ }
+ />
+ Waiting for connection, retrying in{' '}
+ 10 secs...
+
+ }
+ className="cloud-account-setup-form__alert"
+ type="warning"
+ />
+ );
+ case ModalStateEnum.ERROR:
+ return (
+
+
+ {`We couldn't establish a connection to your AWS account. Please try again`}
+
+ }
+ type="error"
+ className="cloud-account-setup-form__alert"
+ />
+ );
+ default:
+ return null;
+ }
+}
+
+export default AlertMessage;
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/CloudAccountSetupModal.style.scss b/frontend/src/container/CloudIntegrationPage/HeroSection/components/CloudAccountSetupModal.style.scss
new file mode 100644
index 00000000000..41ce437d531
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/CloudAccountSetupModal.style.scss
@@ -0,0 +1,221 @@
+.cloud-account-setup-modal {
+ .account-setup-modal-footer {
+ &__confirm-button {
+ background: var(--bg-robin-500);
+ color: var(--bg-vanilla-100);
+ font-size: 12px;
+ font-weight: 500;
+ }
+ &__confirm-selection-count {
+ font-family: 'Geist Mono';
+ }
+ &__close-button {
+ background: var(--bg-slate-400);
+ border-radius: 2px;
+ color: var(--bg-vanilla-100);
+ font-family: 'Inter';
+ font-size: 12px;
+ font-weight: 500;
+ }
+ }
+
+ .cloud-account-setup-form {
+ .disabled {
+ opacity: 0.4;
+ }
+ &,
+ &__content {
+ display: flex;
+ flex-direction: column;
+ gap: 38px;
+ }
+ &__alert {
+ &.ant-alert {
+ padding: 12px;
+ border-radius: 6px;
+ font-size: 14px;
+ line-height: 22px; /* 157.143% */
+ letter-spacing: -0.07px;
+ }
+ &.ant-alert-error {
+ color: var(--bg-sakura-400);
+ border: 1px solid rgba(242, 71, 105, 0.1);
+ background: rgba(242, 71, 105, 0.1);
+ }
+ &.ant-alert-warning {
+ color: var(--bg-amber-400);
+ border: 1px solid rgba(255, 205, 86, 0.1);
+ background: rgba(255, 205, 86, 0.1);
+ }
+ &-message {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ .retry-time {
+ font-family: 'Geist Mono';
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 22px;
+ letter-spacing: -0.07px;
+ }
+ }
+ }
+ &__form-group {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ }
+ &__title {
+ color: var(--bg-vanilla-100);
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ letter-spacing: -0.07px;
+ }
+ &__description {
+ color: var(--bg-vanilla-400);
+ font-size: 12px;
+ font-weight: 400;
+ line-height: 18px;
+ letter-spacing: -0.06px;
+ }
+ &__select {
+ .ant-select-selection-item {
+ color: var(--bg-vanilla-100);
+ font-size: 14px;
+ line-height: 20px;
+ letter-spacing: -0.07px;
+ }
+ }
+ &__form-item {
+ margin: 0;
+ }
+ &__include-all-regions-switch {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ color: var(--bg-vanilla-400);
+ font-size: 12px;
+ line-height: 18px;
+ letter-spacing: -0.06px;
+ margin-bottom: 12px;
+ &-label {
+ background-color: transparent;
+ border: none;
+ color: var(--bg-vanilla-400);
+ font-size: 12px;
+ line-height: 18px;
+ letter-spacing: -0.06px;
+ cursor: pointer;
+ }
+ }
+ &__note {
+ padding: 12px;
+ color: var(--bg-robin-400);
+ font-size: 12px;
+ line-height: 22px;
+ letter-spacing: -0.06px;
+ border-radius: 4px;
+ border: 1px solid rgba(78, 116, 248, 0.1);
+ background: rgba(78, 116, 248, 0.1);
+ }
+ &__submit-button {
+ border-radius: 2px;
+ background: var(--bg-robin-500);
+ color: var(--bg-vanilla-100);
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ &-content {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ }
+ &:disabled {
+ opacity: 0.4;
+ }
+ }
+ }
+}
+
+.lightMode {
+ .cloud-account-setup-modal {
+ .account-setup-modal-footer {
+ &__confirm-button {
+ background: var(--bg-robin-500);
+ color: var(--bg-vanilla-100);
+ }
+
+ &__close-button {
+ background: var(--bg-vanilla-100);
+ border: 1px solid var(--bg-vanilla-300);
+ color: var(--bg-ink-400);
+
+ &:hover {
+ border-color: var(--bg-vanilla-400);
+ color: var(--bg-ink-500);
+ }
+ }
+ }
+
+ .cloud-account-setup-form {
+ &__title {
+ color: var(--bg-ink-500);
+ }
+
+ &__description {
+ color: var(--bg-ink-400);
+ }
+
+ &__select {
+ .ant-select-selection-item {
+ color: var(--bg-ink-500);
+ }
+ }
+
+ &__include-all-regions-switch {
+ color: var(--bg-ink-400);
+
+ &-label {
+ color: var(--bg-ink-400);
+
+ &:hover {
+ color: var(--bg-ink-500);
+ }
+ }
+ }
+
+ &__note {
+ color: var(--bg-robin-500);
+ border: 1px solid rgba(78, 116, 248, 0.2);
+ background: rgba(78, 116, 248, 0.1);
+ }
+
+ &__submit-button {
+ background: var(--bg-robin-500);
+ color: var(--bg-vanilla-100);
+ }
+
+ &__alert {
+ &.ant-alert-error {
+ color: var(--bg-cherry-500);
+ border: 1px solid rgba(242, 71, 105, 0.2);
+ background: rgba(242, 71, 105, 0.1);
+ }
+
+ &.ant-alert-warning {
+ color: var(--bg-amber-500);
+ border: 1px solid rgba(255, 205, 86, 0.2);
+ background: rgba(255, 205, 86, 0.1);
+ }
+
+ &-message {
+ .retry-time {
+ color: var(--bg-ink-500);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/CloudAccountSetupModal.tsx b/frontend/src/container/CloudIntegrationPage/HeroSection/components/CloudAccountSetupModal.tsx
new file mode 100644
index 00000000000..b00ef0033f4
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/CloudAccountSetupModal.tsx
@@ -0,0 +1,185 @@
+import './CloudAccountSetupModal.style.scss';
+
+import { Color } from '@signozhq/design-tokens';
+import SignozModal from 'components/SignozModal/SignozModal';
+import ROUTES from 'constants/routes';
+import { useIntegrationModal } from 'hooks/integrations/aws/useIntegrationModal';
+import { SquareArrowOutUpRight } from 'lucide-react';
+import { useCallback } from 'react';
+import { useNavigate } from 'react-router-dom-v5-compat';
+
+import {
+ ActiveViewEnum,
+ IntegrationModalProps,
+ ModalStateEnum,
+} from '../types';
+import { RegionForm } from './RegionForm';
+import { RegionSelector } from './RegionSelector';
+import { SuccessView } from './SuccessView';
+
+function CloudAccountSetupModal({
+ isOpen,
+ onClose,
+}: IntegrationModalProps): JSX.Element {
+ const {
+ form,
+ modalState,
+ setModalState,
+ isLoading,
+ activeView,
+ selectedRegions,
+ includeAllRegions,
+ isGeneratingUrl,
+ setSelectedRegions,
+ setIncludeAllRegions,
+ handleIncludeAllRegionsChange,
+ handleRegionSelect,
+ handleSubmit,
+ handleClose,
+ setActiveView,
+ allRegions,
+ accountId,
+ selectedDeploymentRegion,
+ handleRegionChange,
+ } = useIntegrationModal({ onClose });
+
+ const renderContent = useCallback(() => {
+ if (modalState === ModalStateEnum.SUCCESS) {
+ return ;
+ }
+
+ if (activeView === ActiveViewEnum.SELECT_REGIONS) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+ }, [
+ modalState,
+ activeView,
+ form,
+ setModalState,
+ selectedRegions,
+ includeAllRegions,
+ handleIncludeAllRegionsChange,
+ handleRegionSelect,
+ handleSubmit,
+ accountId,
+ selectedDeploymentRegion,
+ handleRegionChange,
+ setSelectedRegions,
+ setIncludeAllRegions,
+ ]);
+
+ const getSelectedRegionsCount = useCallback(
+ (): number =>
+ selectedRegions.includes('all') ? allRegions.length : selectedRegions.length,
+ [selectedRegions, allRegions],
+ );
+
+ const navigate = useNavigate();
+ const handleGoToDashboards = useCallback((): void => {
+ navigate(ROUTES.ALL_DASHBOARD);
+ }, [navigate]);
+
+ const getModalConfig = useCallback(() => {
+ // Handle success state first
+ if (modalState === ModalStateEnum.SUCCESS) {
+ return {
+ title: 'AWS Webservice Integration',
+ okText: (
+
+ Go to Dashboards
+
+ ),
+ block: true,
+ onOk: handleGoToDashboards,
+ cancelButtonProps: { style: { display: 'none' } },
+ disabled: false,
+ };
+ }
+
+ // Handle other views
+ const viewConfigs = {
+ [ActiveViewEnum.FORM]: {
+ title: 'Add AWS Account',
+ okText: (
+
+ Launch Cloud Formation Template{' '}
+
+
+ ),
+ onOk: handleSubmit,
+ disabled:
+ isLoading || isGeneratingUrl || modalState === ModalStateEnum.WAITING,
+ cancelButtonProps: { style: { display: 'none' } },
+ },
+ [ActiveViewEnum.SELECT_REGIONS]: {
+ title: 'Which regions do you want to monitor?',
+ okText: `Confirm Selection (${getSelectedRegionsCount()})`,
+ onOk: (): void => setActiveView(ActiveViewEnum.FORM),
+ isLoading: isLoading || isGeneratingUrl,
+ cancelButtonProps: { style: { display: 'block' } },
+ disabled: false,
+ },
+ };
+
+ return viewConfigs[activeView];
+ }, [
+ modalState,
+ handleSubmit,
+ getSelectedRegionsCount,
+ isLoading,
+ isGeneratingUrl,
+ activeView,
+ handleGoToDashboards,
+ setActiveView,
+ ]);
+
+ const modalConfig = getModalConfig();
+
+ return (
+
+ {renderContent()}
+
+ );
+}
+
+export default CloudAccountSetupModal;
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/IntegrateNowFormSections.tsx b/frontend/src/container/CloudIntegrationPage/HeroSection/components/IntegrateNowFormSections.tsx
new file mode 100644
index 00000000000..40a52bdb445
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/IntegrateNowFormSections.tsx
@@ -0,0 +1,122 @@
+import { Color } from '@signozhq/design-tokens';
+import { Form, Select, Switch } from 'antd';
+import { ChevronDown } from 'lucide-react';
+import { Region } from 'types/regions';
+
+// Form section components
+function RegionDeploymentSection({
+ regions,
+}: {
+ regions: Region[];
+}): JSX.Element {
+ return (
+
+
+ Where should we deploy the SigNoz Cloud stack?
+
+
+ Choose the AWS region for CloudFormation stack deployment
+
+
+ }
+ style={{ height: '44px' }}
+ className="cloud-account-setup-form__select"
+ >
+ {regions.flatMap((region) =>
+ region.subRegions.map((subRegion) => (
+
+ {subRegion.displayName}
+
+ )),
+ )}
+
+
+
+ );
+}
+
+function MonitoringRegionsSection({
+ includeAllRegions,
+ selectedRegions,
+ onIncludeAllRegionsChange,
+ getRegionPreviewText,
+ onRegionSelect,
+}: {
+ includeAllRegions: boolean;
+ selectedRegions: string[];
+ onIncludeAllRegionsChange: (checked: boolean) => void;
+ getRegionPreviewText: (regions: string[]) => string[];
+ onRegionSelect: () => void;
+}): JSX.Element {
+ return (
+
+
+ Which regions do you want to monitor?
+
+
+ Choose only the regions you want SigNoz to monitor. You can enable all at
+ once, or pick specific ones:
+
+
=> {
+ if (selectedRegions.length === 0) {
+ throw new Error('Please select at least one region to monitor');
+ }
+ },
+ message: 'Please select at least one region to monitor',
+ },
+ ]}
+ className="cloud-account-setup-form__form-item"
+ >
+
+
+ onIncludeAllRegionsChange(!includeAllRegions)}
+ >
+ Include all regions
+
+
+
+
+
+ );
+}
+
+function ComplianceNote(): JSX.Element {
+ return (
+
+
+ Note: Some organizations may require the CloudFormation stack to be deployed
+ in the same region as their primary infrastructure for compliance or latency
+ reasons.
+
+
+ );
+}
+
+export { ComplianceNote, MonitoringRegionsSection, RegionDeploymentSection };
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/RegionForm.tsx b/frontend/src/container/CloudIntegrationPage/HeroSection/components/RegionForm.tsx
new file mode 100644
index 00000000000..9539278f81c
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/RegionForm.tsx
@@ -0,0 +1,168 @@
+import { Color } from '@signozhq/design-tokens';
+import { Form, Select, Switch } from 'antd';
+import cx from 'classnames';
+import { useAccountStatus } from 'hooks/integrations/aws/useAccountStatus';
+import { ChevronDown } from 'lucide-react';
+import { useRef } from 'react';
+import { AccountStatusResponse } from 'types/api/integrations/aws';
+
+import { regions } from '../../ServicesSection/data';
+import { ModalStateEnum, RegionFormProps } from '../types';
+import AlertMessage from './AlertMessage';
+
+const allRegions = (): string[] =>
+ regions.flatMap((r) => r.subRegions.map((sr) => sr.name));
+
+const getRegionPreviewText = (regions: string[]): string[] => {
+ if (regions.includes('all')) {
+ return allRegions();
+ }
+ return regions;
+};
+
+export function RegionForm({
+ form,
+ modalState,
+ setModalState,
+ selectedRegions,
+ includeAllRegions,
+ onIncludeAllRegionsChange,
+ onRegionSelect,
+ onSubmit,
+ accountId,
+ selectedDeploymentRegion,
+ handleRegionChange,
+}: RegionFormProps): JSX.Element {
+ const startTimeRef = useRef(Date.now());
+ const refetchInterval = 10 * 1000;
+ const errorTimeout = 5 * 60 * 1000;
+
+ const { isLoading: isAccountStatusLoading } = useAccountStatus(accountId, {
+ refetchInterval,
+ enabled: !!accountId && modalState === ModalStateEnum.WAITING,
+ onSuccess: (data: AccountStatusResponse) => {
+ if (data.data.status.integration.last_heartbeat_ts_ms !== null) {
+ setModalState(ModalStateEnum.SUCCESS);
+ } else if (Date.now() - startTimeRef.current >= errorTimeout) {
+ // 5 minutes in milliseconds
+ setModalState(ModalStateEnum.ERROR);
+ }
+ },
+ onError: () => {
+ setModalState(ModalStateEnum.ERROR);
+ },
+ });
+
+ const isFormDisabled =
+ modalState === ModalStateEnum.WAITING || isAccountStatusLoading;
+
+ return (
+
+ );
+}
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/RegionSelector.style.scss b/frontend/src/container/CloudIntegrationPage/HeroSection/components/RegionSelector.style.scss
new file mode 100644
index 00000000000..50715c73a9d
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/RegionSelector.style.scss
@@ -0,0 +1,21 @@
+.select-all {
+ margin-bottom: 20px;
+}
+
+.regions-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(145px, 1fr));
+ gap: 8px;
+}
+
+.region-group {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.region-group label {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+}
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/RegionSelector.tsx b/frontend/src/container/CloudIntegrationPage/HeroSection/components/RegionSelector.tsx
new file mode 100644
index 00000000000..e5f2a0949fb
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/RegionSelector.tsx
@@ -0,0 +1,64 @@
+import './RegionSelector.style.scss';
+
+import { Checkbox } from 'antd';
+import { useRegionSelection } from 'hooks/integrations/aws/useRegionSelection';
+import { Dispatch, SetStateAction } from 'react';
+
+import { regions } from '../../ServicesSection/data';
+
+export function RegionSelector({
+ selectedRegions,
+ setSelectedRegions,
+ setIncludeAllRegions,
+}: {
+ selectedRegions: string[];
+ setSelectedRegions: Dispatch>;
+ setIncludeAllRegions: Dispatch>;
+}): JSX.Element {
+ const {
+ allRegionIds,
+ handleSelectAll,
+ handleRegionSelect,
+ } = useRegionSelection({
+ selectedRegions,
+ setSelectedRegions,
+ setIncludeAllRegions,
+ });
+
+ return (
+
+
+ 20 &&
+ selectedRegions.length < allRegionIds.length
+ }
+ onChange={(e): void => handleSelectAll(e.target.checked)}
+ >
+ Select All Regions
+
+
+
+
+ {regions.map((region) => (
+
+
{region.name}
+ {region.subRegions.map((subRegion) => (
+ handleRegionSelect(subRegion.id)}
+ >
+ {subRegion.name}
+
+ ))}
+
+ ))}
+
+
+ );
+}
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/SuccessView.style.scss b/frontend/src/container/CloudIntegrationPage/HeroSection/components/SuccessView.style.scss
new file mode 100644
index 00000000000..c57a831b14b
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/SuccessView.style.scss
@@ -0,0 +1,156 @@
+.cloud-account-setup-success-view {
+ display: flex;
+ flex-direction: column;
+ gap: 40px;
+ text-align: center;
+ padding-top: 34px;
+ p,
+ h3,
+ h4 {
+ margin: 0;
+ }
+ &__content {
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ .cloud-account-setup-success-view {
+ &__title {
+ h3 {
+ color: var(--bg-vanilla-100);
+ font-size: 20px;
+ font-weight: 500;
+ line-height: 32px;
+ }
+ }
+ &__description {
+ color: var(--bg-vanilla-400);
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 20px;
+ letter-spacing: -0.07px;
+ }
+ }
+ }
+ &__what-next {
+ display: flex;
+ flex-direction: column;
+ gap: 18px;
+ text-align: left;
+ &-title {
+ color: var(--bg-slate-50);
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 18px;
+ letter-spacing: 0.88px;
+ text-transform: uppercase;
+ }
+ .what-next-items-wrapper {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ &__item {
+ display: flex;
+ gap: 10px;
+ align-items: baseline;
+ &.ant-alert {
+ padding: 14px;
+ border-radius: 8px;
+ font-size: 14px;
+ line-height: 20px; /* 142.857% */
+ letter-spacing: -0.21px;
+ }
+ &.ant-alert-info {
+ border: 1px solid rgba(63, 94, 204, 0.5);
+ background: rgba(78, 116, 248, 0.2);
+ color: var(--bg-robin-400);
+ }
+
+ .what-next-item {
+ color: var(--bg-robin-400);
+ &-bullet-icon {
+ font-size: 20px;
+ line-height: 20px;
+ }
+ &-text {
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ letter-spacing: -0.21px;
+ }
+ }
+ }
+ }
+ }
+ &__footer {
+ padding-top: 18px;
+ .ant-btn {
+ background: var(--bg-robin-500);
+ color: var(--bg-vanilla-100);
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ height: 36px;
+ }
+ }
+}
+.lottie-container {
+ position: absolute;
+ width: 743.5px;
+ height: 990.342px;
+ top: -100px;
+ left: -36px;
+ z-index: 1;
+}
+
+.lightMode {
+ .cloud-account-setup-success-view {
+ &__content {
+ .cloud-account-setup-success-view {
+ &__title {
+ h3 {
+ color: var(--bg-ink-500);
+ }
+ }
+
+ &__description {
+ color: var(--bg-ink-400);
+ }
+ }
+ }
+
+ &__what-next {
+ &-title {
+ color: var(--bg-ink-500);
+ }
+
+ .what-next-items-wrapper {
+ &__item {
+ &.ant-alert-info {
+ border: 1px solid rgba(63, 94, 204, 0.2);
+ background: rgba(78, 116, 248, 0.1);
+ color: var(--bg-robin-500);
+ }
+
+ .what-next-item {
+ color: var(--bg-robin-500);
+
+ &-text {
+ color: var(--bg-robin-500);
+ }
+ }
+ }
+ }
+ }
+
+ &__footer {
+ .ant-btn {
+ background: var(--bg-robin-500);
+ color: var(--bg-vanilla-100);
+
+ &:hover {
+ background: var(--bg-robin-400);
+ }
+ }
+ }
+ }
+}
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/SuccessView.tsx b/frontend/src/container/CloudIntegrationPage/HeroSection/components/SuccessView.tsx
new file mode 100644
index 00000000000..7cd22870d5a
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/SuccessView.tsx
@@ -0,0 +1,78 @@
+import './SuccessView.style.scss';
+
+import { Alert } from 'antd';
+import integrationsSuccess from 'assets/Lotties/integrations-success.json';
+import { useState } from 'react';
+import Lottie from 'react-lottie';
+
+export function SuccessView(): JSX.Element {
+ const [isAnimationComplete, setIsAnimationComplete] = useState(false);
+
+ const defaultOptions = {
+ loop: false,
+ autoplay: true,
+ animationData: integrationsSuccess,
+ rendererSettings: {
+ preserveAspectRatio: 'xMidYMid slice',
+ },
+ };
+
+ return (
+ <>
+ {!isAnimationComplete && (
+
+ setIsAnimationComplete(true),
+ },
+ ]}
+ />
+
+ )}
+
+
+
+
+
+
+
🎉 Success!
+ Your AWS Web Service integration is all set.
+
+
+
Your observability journey is off to a great start.
+
Now that your data is flowing, here’s what you can do next:
+
+
+
+
+ WHAT NEXT
+
+
+ {[
+ 'Understand your AWS services with SigNoz’s out-of-the-box dashboards',
+ 'Set up alerts for real-time monitoring.',
+ 'Track logs and traces.',
+ ].map((item) => (
+
+ •
+ {item}
+
+ }
+ type="info"
+ className="what-next-items-wrapper__item"
+ />
+ ))}
+
+
+
+ >
+ );
+}
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/components/RegionForm.tsx b/frontend/src/container/CloudIntegrationPage/HeroSection/components/components/RegionForm.tsx
new file mode 100644
index 00000000000..e636ef4dae5
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/components/RegionForm.tsx
@@ -0,0 +1,183 @@
+import { Color } from '@signozhq/design-tokens';
+import { Button, Form, Select, Switch } from 'antd';
+import cx from 'classnames';
+import { useAccountStatus } from 'hooks/integrations/aws/useAccountStatus';
+import { ChevronDown, SquareArrowOutUpRight } from 'lucide-react';
+import { AccountStatusResponse } from 'types/api/integrations/aws';
+
+import { regions } from '../../ServicesSection/data';
+import AlertMessage from '../AlertMessage';
+import { ModalStateEnum, RegionFormProps } from '../types';
+
+const allRegions = (): string[] =>
+ regions.flatMap((r) => r.subRegions.map((sr) => sr.name));
+
+const getRegionPreviewText = (regions: string[]): string[] => {
+ if (regions.includes('all')) {
+ return allRegions();
+ }
+ return regions;
+};
+
+export function RegionForm({
+ form,
+ modalState,
+ setModalState,
+ selectedRegions,
+ includeAllRegions,
+ isLoading,
+ isGeneratingUrl,
+ onIncludeAllRegionsChange,
+ onRegionSelect,
+ onSubmit,
+ accountId,
+ selectedDeploymentRegion,
+ handleRegionChange,
+}: RegionFormProps): JSX.Element {
+ const { isLoading: isAccountStatusLoading } = useAccountStatus(
+ accountId ?? null,
+ {
+ refetchInterval: 5000,
+ enabled: !!accountId && modalState === ModalStateEnum.WAITING,
+ onSuccess: (data: AccountStatusResponse) => {
+ if (data.data.status.integration.last_heartbeat_ts_ms !== null) {
+ setModalState(ModalStateEnum.SUCCESS);
+ }
+ },
+ onError: () => {
+ setModalState(ModalStateEnum.ERROR);
+ },
+ },
+ );
+
+ const isFormDisabled =
+ modalState === ModalStateEnum.WAITING || isAccountStatusLoading;
+
+ return (
+
+ );
+}
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/components/types.ts b/frontend/src/container/CloudIntegrationPage/HeroSection/components/types.ts
new file mode 100644
index 00000000000..79645117d6d
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/components/types.ts
@@ -0,0 +1,35 @@
+import { FormInstance } from 'antd';
+import { Dispatch, SetStateAction } from 'react';
+
+export enum ActiveViewEnum {
+ SELECT_REGIONS = 'select-regions',
+ FORM = 'form',
+}
+
+export enum ModalStateEnum {
+ FORM = 'form',
+ WAITING = 'waiting',
+ ERROR = 'error',
+ SUCCESS = 'success',
+}
+
+export interface RegionFormProps {
+ form: FormInstance;
+ modalState: ModalStateEnum;
+ setModalState: Dispatch>;
+ selectedRegions: string[];
+ includeAllRegions: boolean;
+ isLoading: boolean;
+ isGeneratingUrl: boolean;
+ onIncludeAllRegionsChange: (checked: boolean) => void;
+ onRegionSelect: () => void;
+ onSubmit: () => Promise;
+ accountId?: string;
+ selectedDeploymentRegion: string | undefined;
+ handleRegionChange: (value: string) => void;
+}
+
+export interface IntegrationModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+}
diff --git a/frontend/src/container/CloudIntegrationPage/HeroSection/types.ts b/frontend/src/container/CloudIntegrationPage/HeroSection/types.ts
new file mode 100644
index 00000000000..0bda02de154
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/HeroSection/types.ts
@@ -0,0 +1,33 @@
+import { FormInstance } from 'antd';
+import { Dispatch, SetStateAction } from 'react';
+
+export enum ActiveViewEnum {
+ SELECT_REGIONS = 'select-regions',
+ FORM = 'form',
+}
+
+export enum ModalStateEnum {
+ FORM = 'form',
+ WAITING = 'waiting',
+ ERROR = 'error',
+ SUCCESS = 'success',
+}
+
+export interface RegionFormProps {
+ form: FormInstance;
+ modalState: ModalStateEnum;
+ setModalState: Dispatch>;
+ selectedRegions: string[];
+ includeAllRegions: boolean;
+ onIncludeAllRegionsChange: (checked: boolean) => void;
+ onRegionSelect: () => void;
+ onSubmit: () => Promise;
+ accountId?: string;
+ selectedDeploymentRegion: string | undefined;
+ handleRegionChange: (value: string) => void;
+}
+
+export interface IntegrationModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+}
diff --git a/frontend/src/container/CloudIntegrationPage/ServicesSection/ConfigureServiceModal.styles.scss b/frontend/src/container/CloudIntegrationPage/ServicesSection/ConfigureServiceModal.styles.scss
new file mode 100644
index 00000000000..dbeb90c5357
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/ServicesSection/ConfigureServiceModal.styles.scss
@@ -0,0 +1,89 @@
+.configure-service-modal {
+ &__body {
+ display: flex;
+ flex-direction: column;
+ border-radius: 3px;
+ border: 1px solid var(--bg-slate-500);
+ padding: 14px;
+
+ &-regions-switch-switch {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+
+ &-label {
+ color: var(--bg-vanilla-100);
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ letter-spacing: -0.07px;
+ }
+ }
+
+ &-switch-description {
+ margin-top: 4px;
+ color: var(--bg-vanilla-400);
+ font-size: 12px;
+ font-weight: 400;
+ line-height: 18px;
+ letter-spacing: -0.06px;
+ }
+ &-form-item {
+ &:last-child {
+ margin-bottom: 0px;
+ }
+ }
+ }
+ .ant-modal-body {
+ padding-bottom: 0;
+ }
+ .ant-modal-footer {
+ margin: 0;
+ padding-bottom: 12px;
+ }
+}
+
+.lightMode {
+ .configure-service-modal {
+ &__body {
+ border-color: var(--bg-vanilla-300);
+
+ &-regions-switch-switch {
+ &-label {
+ color: var(--bg-ink-500);
+ }
+ }
+
+ &-switch-description {
+ color: var(--bg-ink-400);
+ }
+ }
+
+ .ant-btn {
+ &.ant-btn-default {
+ background: var(--bg-vanilla-100);
+ border: 1px solid var(--bg-vanilla-300);
+ color: var(--bg-ink-400);
+
+ &:hover {
+ border-color: var(--bg-vanilla-400);
+ color: var(--bg-ink-500);
+ }
+ }
+
+ &.ant-btn-primary {
+ // Keep primary button same as dark mode
+ background: var(--bg-robin-500);
+ color: var(--bg-vanilla-100);
+
+ &:hover {
+ background: var(--bg-robin-400);
+ }
+
+ &:disabled {
+ opacity: 0.6;
+ }
+ }
+ }
+ }
+}
diff --git a/frontend/src/container/CloudIntegrationPage/ServicesSection/ConfigureServiceModal.tsx b/frontend/src/container/CloudIntegrationPage/ServicesSection/ConfigureServiceModal.tsx
new file mode 100644
index 00000000000..ad417a29cec
--- /dev/null
+++ b/frontend/src/container/CloudIntegrationPage/ServicesSection/ConfigureServiceModal.tsx
@@ -0,0 +1,199 @@
+import './ConfigureServiceModal.styles.scss';
+
+import { Form, Switch } from 'antd';
+import SignozModal from 'components/SignozModal/SignozModal';
+import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
+import {
+ ServiceConfig,
+ SupportedSignals,
+} from 'container/CloudIntegrationPage/ServicesSection/types';
+import { useUpdateServiceConfig } from 'hooks/integrations/aws/useUpdateServiceConfig';
+import { useCallback, useMemo, useState } from 'react';
+import { useQueryClient } from 'react-query';
+
+interface IConfigureServiceModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ serviceName: string;
+ serviceId: string;
+ cloudAccountId: string;
+ supportedSignals: SupportedSignals;
+ initialConfig?: ServiceConfig;
+}
+
+function ConfigureServiceModal({
+ isOpen,
+ onClose,
+ serviceName,
+ serviceId,
+ cloudAccountId,
+ initialConfig,
+ supportedSignals,
+}: IConfigureServiceModalProps): JSX.Element {
+ const [form] = Form.useForm();
+ const [isLoading, setIsLoading] = useState(false);
+
+ // Track current form values
+ const [currentValues, setCurrentValues] = useState({
+ metrics: initialConfig?.metrics?.enabled || false,
+ logs: initialConfig?.logs?.enabled || false,
+ });
+
+ const {
+ mutate: updateServiceConfig,
+ isLoading: isUpdating,
+ } = useUpdateServiceConfig();
+
+ const queryClient = useQueryClient();
+
+ const handleSubmit = useCallback(async (): Promise => {
+ try {
+ const values = await form.validateFields();
+ setIsLoading(true);
+
+ updateServiceConfig(
+ {
+ serviceId,
+ payload: {
+ cloud_account_id: cloudAccountId,
+ config: {
+ logs: {
+ enabled: values.logs,
+ },
+ metrics: {
+ enabled: values.metrics,
+ },
+ },
+ },
+ },
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries([
+ REACT_QUERY_KEY.AWS_SERVICE_DETAILS,
+ serviceId,
+ ]);
+ onClose();
+ },
+ onError: (error) => {
+ console.error('Failed to update service config:', error);
+ },
+ },
+ );
+ } catch (error) {
+ console.error('Form submission failed:', error);
+ } finally {
+ setIsLoading(false);
+ }
+ }, [
+ form,
+ updateServiceConfig,
+ serviceId,
+ cloudAccountId,
+ queryClient,
+ onClose,
+ ]);
+
+ const isSaveDisabled = useMemo(
+ () => currentValues.metrics === false && currentValues.logs === false,
+ [currentValues],
+ );
+
+ const handleClose = useCallback(() => {
+ form.resetFields();
+ onClose();
+ }, [form, onClose]);
+
+ return (
+ Configure {serviceName}
+ }
+ centered
+ open={isOpen}
+ okText="Save"
+ okButtonProps={{
+ disabled: isSaveDisabled,
+ className: 'account-settings-modal__footer-save-button',
+ loading: isLoading || isUpdating,
+ }}
+ onCancel={handleClose}
+ onOk={handleSubmit}
+ cancelText="Close"
+ cancelButtonProps={{
+ className: 'account-settings-modal__footer-close-button',
+ }}
+ width={672}
+ rootClassName=" configure-service-modal"
+ >
+
+
+ );
+}
+
+ConfigureServiceModal.defaultProps = {
+ initialConfig: {
+ metrics: { enabled: false },
+ logs: { enabled: false },
+ },
+};
+
+export default ConfigureServiceModal;
diff --git a/frontend/src/container/CloudIntegrationPage/ServicesSection/ServiceDetails.tsx b/frontend/src/container/CloudIntegrationPage/ServicesSection/ServiceDetails.tsx
index c0a68c5ff59..e1df0b91134 100644
--- a/frontend/src/container/CloudIntegrationPage/ServicesSection/ServiceDetails.tsx
+++ b/frontend/src/container/CloudIntegrationPage/ServicesSection/ServiceDetails.tsx
@@ -1,44 +1,133 @@
import { Color } from '@signozhq/design-tokens';
import { Button, Tabs, TabsProps } from 'antd';
+import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer';
+import Spinner from 'components/Spinner';
+import CloudServiceDashboards from 'container/CloudIntegrationPage/ServicesSection/CloudServiceDashboards';
+import CloudServiceDataCollected from 'container/CloudIntegrationPage/ServicesSection/CloudServiceDataCollected';
+import { IServiceStatus } from 'container/CloudIntegrationPage/ServicesSection/types';
+import dayjs from 'dayjs';
+import { useServiceDetails } from 'hooks/integrations/aws/useServiceDetails';
+import useUrlQuery from 'hooks/useUrlQuery';
import { Wrench } from 'lucide-react';
+import { useState } from 'react';
-import CloudServiceDashboards from './CloudServiceDashboards';
-import CloudServiceDataCollected from './CloudServiceDataCollected';
-import { ServiceData } from './types';
+import ConfigureServiceModal from './ConfigureServiceModal';
+
+const getStatus = (
+ logsLastReceivedTimestamp: number | undefined,
+ metricsLastReceivedTimestamp: number | undefined,
+): { text: string; className: string } => {
+ if (!logsLastReceivedTimestamp && !metricsLastReceivedTimestamp) {
+ return { text: 'No Data Yet', className: 'service-status--no-data' };
+ }
+
+ const latestTimestamp = Math.max(
+ logsLastReceivedTimestamp || 0,
+ metricsLastReceivedTimestamp || 0,
+ );
+
+ const isStale = dayjs().diff(dayjs(latestTimestamp), 'minute') > 30;
+
+ if (isStale) {
+ return { text: 'Stale Data', className: 'service-status--stale-data' };
+ }
+
+ return { text: 'Connected', className: 'service-status--connected' };
+};
+
+function ServiceStatus({
+ serviceStatus,
+}: {
+ serviceStatus: IServiceStatus | null;
+}): JSX.Element {
+ const logsLastReceivedTimestamp = serviceStatus?.logs?.last_received_ts_ms;
+ const metricsLastReceivedTimestamp =
+ serviceStatus?.metrics?.last_received_ts_ms;
+
+ const { text, className } = getStatus(
+ logsLastReceivedTimestamp,
+ metricsLastReceivedTimestamp,
+ );
+
+ return {text}
;
+}
+
+function ServiceDetails(): JSX.Element | null {
+ const urlQuery = useUrlQuery();
+ const accountId = urlQuery.get('accountId');
+ const serviceId = urlQuery.get('service');
+ const [isConfigureServiceModalOpen, setIsConfigureServiceModalOpen] = useState(
+ false,
+ );
+
+ const { data: serviceDetailsData, isLoading } = useServiceDetails(
+ serviceId || '',
+ accountId || undefined,
+ );
+
+ if (isLoading) {
+ return ;
+ }
+
+ if (!serviceDetailsData) {
+ return null;
+ }
-function ServiceDetails({ service }: { service: ServiceData }): JSX.Element {
const tabItems: TabsProps['items'] = [
{
key: 'dashboards',
- label: `Dashboards (${service.assets.dashboards.length})`,
- children: ,
+ label: `Dashboards (${serviceDetailsData?.assets.dashboards.length})`,
+ children: ,
},
{
key: 'data-collected',
label: 'Data Collected',
children: (
),
},
];
+
return (
Details
-
-
- Configure
-
+ {serviceDetailsData?.status && (
+
+ )}
+ {!!accountId && (
+ setIsConfigureServiceModalOpen(true)}
+ >
+
+ Configure
+
+ )}
-
{service.overview}
+
+
+
+
setIsConfigureServiceModalOpen(false)}
+ serviceName={serviceDetailsData.title}
+ serviceId={serviceId || ''}
+ cloudAccountId={accountId || ''}
+ initialConfig={serviceDetailsData.config}
+ supportedSignals={serviceDetailsData.supported_signals || {}}
+ />
);
}
diff --git a/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesList.tsx b/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesList.tsx
index 99902b6ddd2..bd802711173 100644
--- a/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesList.tsx
+++ b/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesList.tsx
@@ -1,23 +1,48 @@
+import Spinner from 'components/Spinner';
+import { useGetAccountServices } from 'hooks/integrations/aws/useGetAccountServices';
+import useUrlQuery from 'hooks/useUrlQuery';
+import { useMemo } from 'react';
+import { useNavigate } from 'react-router-dom-v5-compat';
+
import ServiceItem from './ServiceItem';
-import { Service } from './types';
-
-function ServicesList({
- services,
- onClick,
- activeService,
-}: {
- services: Service[];
- onClick: (serviceName: string) => void;
- activeService: string | null;
-}): JSX.Element {
+
+interface ServicesListProps {
+ accountId: string;
+ filter: 'all_services' | 'enabled' | 'available';
+}
+
+function ServicesList({ accountId, filter }: ServicesListProps): JSX.Element {
+ const urlQuery = useUrlQuery();
+ const navigate = useNavigate();
+ const { data: services = [], isLoading } = useGetAccountServices(accountId);
+ const activeService = urlQuery.get('service');
+
+ const handleServiceClick = (serviceId: string): void => {
+ urlQuery.set('service', serviceId);
+ navigate({ search: urlQuery.toString() });
+ };
+
+ const filteredServices = useMemo(() => {
+ if (filter === 'all_services') return services;
+
+ return services.filter((service) => {
+ const isEnabled =
+ service?.config?.logs?.enabled || service?.config?.metrics?.enabled;
+ return filter === 'enabled' ? isEnabled : !isEnabled;
+ });
+ }, [services, filter]);
+
+ if (isLoading) return ;
+ if (!services) return No services found
;
+
return (
- {services.map((service) => (
+ {filteredServices.map((service) => (
))}
diff --git a/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.style.scss b/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.style.scss
index a73e1023b54..45e4f25931c 100644
--- a/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.style.scss
+++ b/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.style.scss
@@ -107,7 +107,34 @@
}
.service-details__right-actions {
display: flex;
+ align-items: center;
gap: 8px;
+ .service-status {
+ display: flex;
+ align-items: center;
+ padding: 2px 8px;
+ font-family: 'Geist Mono';
+ font-size: 12px;
+ font-weight: 500;
+ letter-spacing: 0.54px;
+ border-radius: 2px;
+ line-height: normal;
+ &--connected {
+ border: 1px solid rgba(37, 225, 146, 0.1);
+ background: rgba(37, 225, 146, 0.1);
+ color: var(--bg-forest-400);
+ }
+ &--stale-data {
+ background: rgba(255, 215, 120, 0.1);
+ border: 1px solid rgba(255, 215, 120, 0.1);
+ color: var(--bg-amber-400);
+ }
+ &--no-data {
+ border: 1px solid rgba(234, 109, 113, 0.1);
+ background: rgba(234, 109, 113, 0.1);
+ color: var(--bg-cherry-400);
+ }
+ }
.configure-button {
display: flex;
align-items: center;
@@ -252,3 +279,153 @@
}
}
}
+
+.lightMode {
+ .services-tabs {
+ .ant-tabs-tab {
+ &.ant-tabs-tab-active {
+ .ant-tabs-tab-btn {
+ color: var(--bg-ink-500);
+ }
+ }
+ }
+ }
+
+ .services-filter {
+ .ant-select-selector {
+ background-color: var(--bg-vanilla-100) !important;
+ border-color: var(--bg-vanilla-300) !important;
+ color: var(--bg-ink-400) !important;
+ }
+
+ .ant-select-arrow {
+ color: var(--bg-ink-400);
+ }
+ }
+
+ .service-item {
+ &:not(:last-child) {
+ border-bottom: 1px solid var(--bg-vanilla-300);
+ }
+
+ &.active {
+ background-color: var(--bg-vanilla-300);
+ }
+
+ &__icon-wrapper {
+ background-color: var(--bg-vanilla-100);
+ border-color: var(--bg-vanilla-300);
+ }
+
+ &__title {
+ color: var(--bg-ink-500);
+ }
+ }
+
+ .service-details {
+ &__title-bar {
+ border-bottom: 1px solid var(--bg-vanilla-300);
+
+ .service-details__details-title {
+ color: var(--bg-ink-400);
+ }
+
+ .configure-button {
+ color: var(--bg-ink-400);
+ background: var(--bg-vanilla-100);
+ border-color: var(--bg-vanilla-300);
+
+ &:hover {
+ border-color: var(--bg-vanilla-400);
+ color: var(--bg-ink-500);
+ }
+ }
+
+ .service-status {
+ &--connected {
+ border: 1px solid rgba(37, 225, 146, 0.2);
+ background: rgba(37, 225, 146, 0.1);
+ color: var(--bg-forest-500);
+ }
+
+ &--stale-data {
+ border: 1px solid rgba(255, 215, 120, 0.2);
+ background: rgba(255, 215, 120, 0.1);
+ color: var(--bg-amber-500);
+ }
+
+ &--no-data {
+ border: 1px solid rgba(234, 109, 113, 0.2);
+ background: rgba(234, 109, 113, 0.1);
+ color: var(--bg-cherry-500);
+ }
+ }
+ }
+
+ &__overview {
+ color: var(--bg-ink-400);
+ }
+
+ &__tabs {
+ .ant-tabs {
+ &-tab {
+ &-btn {
+ color: var(--bg-ink-400) !important;
+
+ &[aria-selected='true'] {
+ color: var(--bg-ink-500) !important;
+ }
+ }
+
+ &-active {
+ background: var(--bg-vanilla-300);
+ }
+ }
+
+ &-nav-list {
+ border-color: var(--bg-vanilla-300);
+ background: var(--bg-vanilla-100);
+ }
+ }
+ }
+
+ .cloud-service {
+ &-dashboard-item {
+ &__title {
+ color: var(--bg-ink-500);
+ }
+ }
+
+ &-data-collected {
+ &__table {
+ .ant-table {
+ border-color: var(--bg-vanilla-300);
+
+ .ant-table-thead {
+ > tr > th {
+ color: var(--bg-ink-400);
+ }
+ }
+
+ .ant-table-tbody {
+ > tr {
+ &:nth-child(odd),
+ &:hover > td {
+ background: var(--bg-vanilla-100) !important;
+ }
+
+ > td {
+ color: var(--bg-ink-400);
+ }
+ }
+ }
+ }
+ }
+
+ &__table-heading {
+ color: var(--bg-ink-500);
+ }
+ }
+ }
+ }
+}
diff --git a/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.tsx b/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.tsx
index 99d79cbec92..c9eead772bb 100644
--- a/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.tsx
+++ b/frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.tsx
@@ -3,22 +3,57 @@ import './ServicesTabs.style.scss';
import { Color } from '@signozhq/design-tokens';
import type { SelectProps, TabsProps } from 'antd';
import { Select, Tabs } from 'antd';
-import useUrlQuery from 'hooks/useUrlQuery';
+import { getAwsServices } from 'api/integrations/aws';
+import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { ChevronDown } from 'lucide-react';
-import { useState } from 'react';
-import { useNavigate } from 'react-router-dom-v5-compat';
+import { useMemo, useState } from 'react';
+import { useQuery } from 'react-query';
-import { serviceDetails, services } from './data';
import ServiceDetails from './ServiceDetails';
import ServicesList from './ServicesList';
-const selectOptions: SelectProps['options'] = [
- { value: 'all_services', label: 'All Services (24)' },
- { value: 'enabled', label: 'Enabled (12)' },
- { value: 'available', label: 'Available (12)' },
-];
+interface ServicesFilterProps {
+ accountId: string;
+ onFilterChange: (value: 'all_services' | 'enabled' | 'available') => void;
+}
+
+function ServicesFilter({
+ accountId,
+ onFilterChange,
+}: ServicesFilterProps): JSX.Element | null {
+ const { data: services, isLoading } = useQuery(
+ [REACT_QUERY_KEY.AWS_SERVICES, accountId],
+ () => getAwsServices(accountId),
+ );
+
+ const { enabledCount, availableCount } = useMemo(() => {
+ if (!services) return { enabledCount: 0, availableCount: 0 };
+
+ return services.reduce(
+ (acc, service) => {
+ const isEnabled =
+ service?.config?.logs?.enabled || service?.config?.metrics?.enabled;
+ return {
+ enabledCount: acc.enabledCount + (isEnabled ? 1 : 0),
+ availableCount: acc.availableCount + (isEnabled ? 0 : 1),
+ };
+ },
+ { enabledCount: 0, availableCount: 0 },
+ );
+ }, [services]);
+
+ const selectOptions: SelectProps['options'] = useMemo(
+ () => [
+ { value: 'all_services', label: `All Services (${services?.length || 0})` },
+ { value: 'enabled', label: `Enabled (${enabledCount})` },
+ { value: 'available', label: `Available (${availableCount})` },
+ ],
+ [services, enabledCount, availableCount],
+ );
+
+ if (isLoading) return null;
+ if (!services?.length) return null;
-function ServicesFilter(): JSX.Element {
return (
}
- onChange={(value): void => {
- console.log('selected region:', value);
- }}
+ onChange={onFilterChange}
/>
);
}
-function ServicesSection(): JSX.Element {
- const urlQuery = useUrlQuery();
- const navigate = useNavigate();
- const [activeService, setActiveService] = useState(
- urlQuery.get('service') || serviceDetails[0].id,
- );
+interface ServicesSectionProps {
+ accountId: string;
+}
- const handleServiceClick = (serviceId: string): void => {
- setActiveService(serviceId);
- urlQuery.set('service', serviceId);
- navigate({ search: urlQuery.toString() });
- };
-
- const activeServiceDetails = serviceDetails.find(
- (service) => service.id === activeService,
- );
+function ServicesSection({ accountId }: ServicesSectionProps): JSX.Element {
+ const [activeFilter, setActiveFilter] = useState<
+ 'all_services' | 'enabled' | 'available'
+ >('all_services');
return (
-
-
+
+
- {activeServiceDetails && }
+
);
}
-function ServicesTabs(): JSX.Element {
+interface ServicesTabsProps {
+ accountId: string;
+}
+
+function ServicesTabs({ accountId }: ServicesTabsProps): JSX.Element {
const tabItems: TabsProps['items'] = [
{
key: 'services',
label: 'Services For Integration',
- children: ,
+ children: ,
},
];
diff --git a/frontend/src/container/CloudIntegrationPage/ServicesSection/data.ts b/frontend/src/container/CloudIntegrationPage/ServicesSection/data.ts
index 90b5f290007..978895bb6f6 100644
--- a/frontend/src/container/CloudIntegrationPage/ServicesSection/data.ts
+++ b/frontend/src/container/CloudIntegrationPage/ServicesSection/data.ts
@@ -1,179 +1,144 @@
-import { CloudAccountsData, Service, ServiceData } from './types';
+import { Region } from 'types/regions';
-const services: Service[] = [
+const regions: Region[] = [
{
- id: 'aws-elasticache',
- title: 'AWS ElastiCache',
- icon: '/Logos/aws-dark.svg?a=11',
+ id: 'north-america',
+ name: 'North America',
+ subRegions: [
+ { id: 'us-east-1', name: 'us-east-1', displayName: 'US East (N. Virginia)' },
+ { id: 'us-east-2', name: 'us-east-2', displayName: 'US East (Ohio)' },
+ {
+ id: 'us-west-1',
+ name: 'us-west-1',
+ displayName: 'US West (N. California)',
+ },
+ { id: 'us-west-2', name: 'us-west-2', displayName: 'US West (Oregon)' },
+ {
+ id: 'ca-central-1',
+ name: 'ca-central-1',
+ displayName: 'Canada (Central)',
+ },
+ { id: 'ca-west-1', name: 'ca-west-1', displayName: 'Canada (West)' },
+ ],
},
{
- id: 'amazon-eks',
- title: 'AWS EKS',
- icon: '/Logos/aws-dark.svg?a=21',
+ id: 'africa',
+ name: 'Africa',
+ subRegions: [
+ { id: 'af-south-1', name: 'af-south-1', displayName: 'Africa (Cape Town)' },
+ ],
},
{
- id: 'amazon-dynamo-db',
- title: 'Amazon DynamoDB',
- icon: '/Logos/aws-dark.svg?a=31',
+ id: 'asia-pacific',
+ name: 'Asia Pacific',
+ subRegions: [
+ {
+ id: 'ap-east-1',
+ name: 'ap-east-1',
+ displayName: 'Asia Pacific (Hong Kong)',
+ },
+ {
+ id: 'ap-northeast-1',
+ name: 'ap-northeast-1',
+ displayName: 'Asia Pacific (Tokyo)',
+ },
+ {
+ id: 'ap-northeast-2',
+ name: 'ap-northeast-2',
+ displayName: 'Asia Pacific (Seoul)',
+ },
+ {
+ id: 'ap-northeast-3',
+ name: 'ap-northeast-3',
+ displayName: 'Asia Pacific (Osaka)',
+ },
+ {
+ id: 'ap-south-1',
+ name: 'ap-south-1',
+ displayName: 'Asia Pacific (Mumbai)',
+ },
+ {
+ id: 'ap-south-2',
+ name: 'ap-south-2',
+ displayName: 'Asia Pacific (Hyderabad)',
+ },
+ {
+ id: 'ap-southeast-1',
+ name: 'ap-southeast-1',
+ displayName: 'Asia Pacific (Singapore)',
+ },
+ {
+ id: 'ap-southeast-2',
+ name: 'ap-southeast-2',
+ displayName: 'Asia Pacific (Sydney)',
+ },
+ {
+ id: 'ap-southeast-3',
+ name: 'ap-southeast-3',
+ displayName: 'Asia Pacific (Jakarta)',
+ },
+ {
+ id: 'ap-southeast-4',
+ name: 'ap-southeast-4',
+ displayName: 'Asia Pacific (Melbourne)',
+ },
+ {
+ id: 'ap-southeast-5',
+ name: 'ap-southeast-5',
+ displayName: 'Asia Pacific (Auckland)',
+ },
+ ],
},
-];
-
-const serviceDetails: ServiceData[] = [
- // For aws-elasticache
{
- id: 'aws-elasticache',
- title: 'AWS ElastiCache',
- icon: '/Logos/aws-dark.svg?a=1',
- overview:
- '**AWS ElastiCache** is a fully managed, Redis and Memcached-compatible service',
- assets: {
- dashboards: [
- {
- id: 'elasticache-overview',
- url: '/dashboard/elasticache',
- title: 'ElastiCache Overview',
- description: 'Monitor your ElastiCache clusters performance and health',
- image:
- 'https://cdn1.dronahq.com/wp-content/uploads/2024/08/Dashboard-Image-Final.webp',
- },
- ],
- },
- data_collected: {
- metrics: [
- {
- name: 'cache_hits',
- type: 'counter',
- unit: 'ops',
- },
- {
- name: 'memory_usage',
- type: 'gauge',
- unit: 'bytes',
- },
- ],
- },
+ id: 'europe',
+ name: 'Europe',
+ subRegions: [
+ {
+ id: 'eu-central-1',
+ name: 'eu-central-1',
+ displayName: 'Europe (Frankfurt)',
+ },
+ { id: 'eu-central-2', name: 'eu-central-2', displayName: 'Europe (Zurich)' },
+ { id: 'eu-north-1', name: 'eu-north-1', displayName: 'Europe (Stockholm)' },
+ { id: 'eu-south-1', name: 'eu-south-1', displayName: 'Europe (Milan)' },
+ { id: 'eu-south-2', name: 'eu-south-2', displayName: 'Europe (Spain)' },
+ { id: 'eu-west-1', name: 'eu-west-1', displayName: 'Europe (Ireland)' },
+ { id: 'eu-west-2', name: 'eu-west-2', displayName: 'Europe (London)' },
+ { id: 'eu-west-3', name: 'eu-west-3', displayName: 'Europe (Paris)' },
+ ],
},
-
- // For amazon-eks
{
- id: 'amazon-eks',
- title: 'AWS EKS',
- icon: '/Logos/aws-dark.svg?a=2',
- overview:
- 'Amazon Elastic Kubernetes Service (EKS) is a managed Kubernetes service that automates certain aspects of deployment and maintenance for any standard Kubernetes environment. Whether you are migrating an existing Kubernetes application to Amazon EKS, or are deploying a new cluster on Amazon EKS on AWS Outposts',
- assets: {
- dashboards: [
- {
- id: 'eks-cluster-overview',
- url: '/dashboard/eks',
- title: 'EKS Cluster Overview',
- description: 'Monitor your EKS cluster performance and health',
- image:
- 'https://webdashboard.com/wp-content/uploads/2024/08/Power-BI-report-dashboard-sharing-Webdashboard-Screenshot-1536x864.png',
- },
- ],
- },
- data_collected: {
- metrics: [
- {
- name: 'pod_count',
- type: 'gauge',
- unit: 'count',
- },
- {
- name: 'node_cpu_usage',
- type: 'gauge',
- unit: 'percent',
- },
- ],
- logs: [
- {
- name: 'pod_count',
- type: 'gauge',
- path: 'count',
- },
- {
- name: 'node_cpu_usage',
- type: 'gauge',
- path: 'percent',
- },
- ],
- },
+ id: 'middle-east',
+ name: 'Middle East',
+ subRegions: [
+ {
+ id: 'il-central-1',
+ name: 'il-central-1',
+ displayName: 'Israel (Tel Aviv)',
+ },
+ {
+ id: 'me-central-1',
+ name: 'me-central-1',
+ displayName: 'Middle East (UAE)',
+ },
+ {
+ id: 'me-south-1',
+ name: 'me-south-1',
+ displayName: 'Middle East (Bahrain)',
+ },
+ ],
},
-
- // For amazon-dynamo-db
{
- id: 'amazon-dynamo-db',
- title: 'Amazon DynamoDB',
- icon: '/Logos/aws-dark.svg?a=3',
- overview: '**Amazon DynamoDB** is a fully managed NoSQL database service',
- assets: {
- dashboards: [
- {
- id: 'dynamodb-overview',
- url: '/dashboard/dynamodb',
- title: 'DynamoDB Overview',
- description: 'Monitor your DynamoDB tables performance and consumption',
- image: '/Logos/aws-dark.svg?a=3',
- },
- ],
- },
- data_collected: {
- metrics: [
- {
- name: 'read_throughput',
- type: 'gauge',
- unit: 'ops',
- },
- {
- name: 'write_throughput',
- type: 'gauge',
- unit: 'ops',
- },
- ],
- },
+ id: 'south-america',
+ name: 'South America',
+ subRegions: [
+ {
+ id: 'sa-east-1',
+ name: 'sa-east-1',
+ displayName: 'South America (São Paulo)',
+ },
+ ],
},
];
-const cloudAccountsData: CloudAccountsData = {
- accounts: [
- {
- id: '3e585f2d-fd1e-43bf-8a3b-ee9d449cc626',
- cloud_account_id: '443370682259',
- config: {
- regions: ['us-east-1', 'us-east-2', 'us-west-1', 'us-west-2'],
- },
- status: {
- integration: {
- last_heartbeat_ts_ms: 1709825467000,
- },
- },
- },
- {
- id: '7a9b2c3d-4e5f-6g7h-8i9j-0k1l2m3n4o5p',
- cloud_account_id: '123456789012',
- config: {
- regions: ['all'],
- },
- status: {
- integration: {
- last_heartbeat_ts_ms: 1709825467000,
- },
- },
- },
- {
- id: '9p8o7n6m-5l4k-3j2i-1h0g-f4e3d2c1b0a',
- cloud_account_id: '098765432109',
- config: {
- regions: ['eu-west-1', 'eu-central-1', 'ap-southeast-1'],
- },
- status: {
- integration: {
- last_heartbeat_ts_ms: 1709825467000,
- },
- },
- },
- ],
-};
-
-export { cloudAccountsData, serviceDetails, services };
+export { regions };
diff --git a/frontend/src/container/CloudIntegrationPage/ServicesSection/types.ts b/frontend/src/container/CloudIntegrationPage/ServicesSection/types.ts
index 83e6ac83c35..71c276f4bab 100644
--- a/frontend/src/container/CloudIntegrationPage/ServicesSection/types.ts
+++ b/frontend/src/container/CloudIntegrationPage/ServicesSection/types.ts
@@ -2,6 +2,7 @@ interface Service {
id: string;
title: string;
icon: string;
+ config: ServiceConfig;
}
interface Dashboard {
@@ -38,16 +39,22 @@ interface ServiceConfig {
metrics: ConfigStatus;
}
-interface ServiceStatus {
+interface IServiceStatus {
logs: DataStatus | null;
metrics: DataStatus | null;
}
+interface SupportedSignals {
+ metrics: boolean;
+ logs: boolean;
+}
+
interface ServiceData {
id: string;
title: string;
icon: string;
overview: string;
+ supported_signals: SupportedSignals;
assets: {
dashboards: Dashboard[];
};
@@ -55,8 +62,13 @@ interface ServiceData {
logs?: LogField[];
metrics: Metric[];
};
- config?: ServiceConfig; // Optional - included only with account_id
- status?: ServiceStatus; // Optional - included only with account_id
+ config?: ServiceConfig;
+ status?: IServiceStatus;
+}
+
+interface ServiceDetailsResponse {
+ status: 'success';
+ data: ServiceData;
}
interface CloudAccountConfig {
@@ -82,4 +94,42 @@ interface CloudAccountsData {
accounts: CloudAccount[];
}
-export type { CloudAccount, CloudAccountsData, Service, ServiceData };
+interface UpdateServiceConfigPayload {
+ cloud_account_id: string;
+ config: {
+ logs: {
+ enabled: boolean;
+ };
+ metrics: {
+ enabled: boolean;
+ };
+ };
+}
+
+interface UpdateServiceConfigResponse {
+ status: string;
+ data: {
+ id: string;
+ config: {
+ logs: {
+ enabled: boolean;
+ };
+ metrics: {
+ enabled: boolean;
+ };
+ };
+ };
+}
+
+export type {
+ CloudAccount,
+ CloudAccountsData,
+ IServiceStatus,
+ Service,
+ ServiceConfig,
+ ServiceData,
+ ServiceDetailsResponse,
+ SupportedSignals,
+ UpdateServiceConfigPayload,
+ UpdateServiceConfigResponse,
+};
diff --git a/frontend/src/hooks/integrations/aws/useAccountSettingsModal.ts b/frontend/src/hooks/integrations/aws/useAccountSettingsModal.ts
new file mode 100644
index 00000000000..21559537130
--- /dev/null
+++ b/frontend/src/hooks/integrations/aws/useAccountSettingsModal.ts
@@ -0,0 +1,139 @@
+import { Form } from 'antd';
+import { FormInstance } from 'antd/lib';
+import { regions } from 'container/CloudIntegrationPage/ServicesSection/data';
+import { CloudAccount } from 'container/CloudIntegrationPage/ServicesSection/types';
+import { useUpdateAccountConfig } from 'hooks/integrations/aws/useUpdateAccountConfig';
+import { isEqual } from 'lodash-es';
+import {
+ Dispatch,
+ SetStateAction,
+ useCallback,
+ useEffect,
+ useMemo,
+ useState,
+} from 'react';
+import { AccountConfigPayload } from 'types/api/integrations/aws';
+
+interface UseAccountSettingsModalProps {
+ onClose: () => void;
+ account: CloudAccount;
+ setActiveAccount: Dispatch>;
+}
+
+interface UseAccountSettingsModal {
+ form: FormInstance;
+ isLoading: boolean;
+ selectedRegions: string[];
+ includeAllRegions: boolean;
+ isRegionSelectOpen: boolean;
+ isSaveDisabled: boolean;
+ setSelectedRegions: Dispatch>;
+ setIncludeAllRegions: Dispatch>;
+ setIsRegionSelectOpen: Dispatch>;
+ handleIncludeAllRegionsChange: (checked: boolean) => void;
+ handleSubmit: () => Promise;
+ handleClose: () => void;
+}
+
+const allRegions = (): string[] =>
+ regions.flatMap((r) => r.subRegions.map((sr) => sr.name));
+
+const getRegionPreviewText = (regions: string[] | undefined): string[] => {
+ if (!regions) return [];
+ if (regions.includes('all')) {
+ return allRegions();
+ }
+ return regions;
+};
+
+export function useAccountSettingsModal({
+ onClose,
+ account,
+ setActiveAccount,
+}: UseAccountSettingsModalProps): UseAccountSettingsModal {
+ const [form] = Form.useForm();
+ const { mutate: updateConfig, isLoading } = useUpdateAccountConfig();
+ const accountRegions = useMemo(() => account?.config?.regions || [], [
+ account?.config?.regions,
+ ]);
+ const [isInitialRegionsSet, setIsInitialRegionsSet] = useState(false);
+
+ const [selectedRegions, setSelectedRegions] = useState([]);
+ const [includeAllRegions, setIncludeAllRegions] = useState(false);
+ const [isRegionSelectOpen, setIsRegionSelectOpen] = useState(false);
+
+ // Initialize regions from account when modal opens
+ useEffect(() => {
+ if (accountRegions.length > 0 && !isInitialRegionsSet) {
+ setSelectedRegions(accountRegions);
+ setIsInitialRegionsSet(true);
+ setIncludeAllRegions(accountRegions.includes('all'));
+ }
+ }, [accountRegions, isInitialRegionsSet]);
+
+ const handleSubmit = useCallback(async (): Promise => {
+ try {
+ await form.validateFields();
+ const payload: AccountConfigPayload = {
+ config: {
+ regions: selectedRegions,
+ },
+ };
+
+ updateConfig(
+ { accountId: account?.id, payload },
+ {
+ onSuccess: (response) => {
+ setActiveAccount(response.data);
+ onClose();
+ },
+ },
+ );
+ } catch (error) {
+ console.error('Form submission failed:', error);
+ }
+ }, [
+ form,
+ selectedRegions,
+ updateConfig,
+ account?.id,
+ setActiveAccount,
+ onClose,
+ ]);
+
+ const isSaveDisabled = useMemo(
+ () => isEqual(selectedRegions.sort(), accountRegions.sort()),
+ [selectedRegions, accountRegions],
+ );
+
+ const handleIncludeAllRegionsChange = useCallback((checked: boolean): void => {
+ setIncludeAllRegions(checked);
+ if (checked) {
+ setSelectedRegions(['all']);
+ } else {
+ setSelectedRegions([]);
+ }
+ }, []);
+
+ const handleClose = useCallback(() => {
+ setIsRegionSelectOpen(false);
+ onClose();
+ }, [onClose]);
+
+ return {
+ form,
+ isLoading,
+ selectedRegions,
+ includeAllRegions,
+ isRegionSelectOpen,
+ isSaveDisabled,
+ setSelectedRegions,
+ setIncludeAllRegions,
+ setIsRegionSelectOpen,
+ handleIncludeAllRegionsChange,
+ handleSubmit,
+ handleClose,
+ };
+}
+
+export { getRegionPreviewText };
diff --git a/frontend/src/hooks/integrations/aws/useAccountStatus.ts b/frontend/src/hooks/integrations/aws/useAccountStatus.ts
new file mode 100644
index 00000000000..967da31b6de
--- /dev/null
+++ b/frontend/src/hooks/integrations/aws/useAccountStatus.ts
@@ -0,0 +1,23 @@
+import axios from 'api';
+import { AxiosError } from 'axios';
+import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
+import { useQuery, UseQueryOptions, UseQueryResult } from 'react-query';
+import { AccountStatusResponse } from 'types/api/integrations/aws';
+
+export function useAccountStatus(
+ accountId: string | undefined,
+ options: UseQueryOptions,
+): UseQueryResult {
+ return useQuery({
+ queryKey: [REACT_QUERY_KEY.AWS_ACCOUNT_STATUS, accountId],
+ queryFn: async () => {
+ console.log('fetching account status');
+ const response = await axios.get(
+ `/cloud-integrations/aws/accounts/${accountId}/status`,
+ );
+ console.log('fetched account status', response.data);
+ return response.data;
+ },
+ ...options,
+ });
+}
diff --git a/frontend/src/hooks/integrations/aws/useAwsAccounts.ts b/frontend/src/hooks/integrations/aws/useAwsAccounts.ts
new file mode 100644
index 00000000000..9d0fac9666d
--- /dev/null
+++ b/frontend/src/hooks/integrations/aws/useAwsAccounts.ts
@@ -0,0 +1,8 @@
+import { getAwsAccounts } from 'api/integrations/aws';
+import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
+import { CloudAccount } from 'container/CloudIntegrationPage/ServicesSection/types';
+import { useQuery, UseQueryResult } from 'react-query';
+
+export function useAwsAccounts(): UseQueryResult {
+ return useQuery(REACT_QUERY_KEY.AWS_ACCOUNTS, getAwsAccounts);
+}
diff --git a/frontend/src/hooks/integrations/aws/useGenerateConnectionUrl.ts b/frontend/src/hooks/integrations/aws/useGenerateConnectionUrl.ts
new file mode 100644
index 00000000000..f8e197fdff0
--- /dev/null
+++ b/frontend/src/hooks/integrations/aws/useGenerateConnectionUrl.ts
@@ -0,0 +1,21 @@
+import { generateConnectionUrl } from 'api/integrations/aws';
+import { AxiosError } from 'axios';
+import { useMutation, UseMutationResult } from 'react-query';
+import {
+ ConnectionUrlResponse,
+ GenerateConnectionUrlPayload,
+} from 'types/api/integrations/aws';
+
+export function useGenerateConnectionUrl(): UseMutationResult<
+ ConnectionUrlResponse,
+ AxiosError,
+ GenerateConnectionUrlPayload
+> {
+ return useMutation<
+ ConnectionUrlResponse,
+ AxiosError,
+ GenerateConnectionUrlPayload
+ >({
+ mutationFn: generateConnectionUrl,
+ });
+}
diff --git a/frontend/src/hooks/integrations/aws/useGetAccountServices.ts b/frontend/src/hooks/integrations/aws/useGetAccountServices.ts
new file mode 100644
index 00000000000..2de79918dc1
--- /dev/null
+++ b/frontend/src/hooks/integrations/aws/useGetAccountServices.ts
@@ -0,0 +1,12 @@
+import { getAwsServices } from 'api/integrations/aws';
+import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
+import { Service } from 'container/CloudIntegrationPage/ServicesSection/types';
+import { useQuery, UseQueryResult } from 'react-query';
+
+export function useGetAccountServices(
+ accountId?: string,
+): UseQueryResult {
+ return useQuery([REACT_QUERY_KEY.AWS_SERVICES, accountId], () =>
+ getAwsServices(accountId),
+ );
+}
diff --git a/frontend/src/hooks/integrations/aws/useIntegrationModal.ts b/frontend/src/hooks/integrations/aws/useIntegrationModal.ts
new file mode 100644
index 00000000000..e964e89ad10
--- /dev/null
+++ b/frontend/src/hooks/integrations/aws/useIntegrationModal.ts
@@ -0,0 +1,164 @@
+import { Form } from 'antd';
+import { FormInstance } from 'antd/lib';
+import {
+ ActiveViewEnum,
+ ModalStateEnum,
+} from 'container/CloudIntegrationPage/HeroSection/types';
+import { regions } from 'container/CloudIntegrationPage/ServicesSection/data';
+import {
+ Dispatch,
+ SetStateAction,
+ useCallback,
+ useEffect,
+ useMemo,
+ useState,
+} from 'react';
+import {
+ ConnectionUrlResponse,
+ GenerateConnectionUrlPayload,
+} from 'types/api/integrations/aws';
+
+import { useGenerateConnectionUrl } from './useGenerateConnectionUrl';
+
+interface UseIntegrationModalProps {
+ onClose: () => void;
+}
+
+interface UseIntegrationModal {
+ form: FormInstance;
+ modalState: ModalStateEnum;
+ setModalState: Dispatch>;
+ isLoading: boolean;
+ activeView: ActiveViewEnum;
+ selectedRegions: string[];
+ includeAllRegions: boolean;
+ isGeneratingUrl: boolean;
+ setSelectedRegions: Dispatch>;
+ setIncludeAllRegions: Dispatch>;
+ handleIncludeAllRegionsChange: (checked: boolean) => void;
+ handleRegionSelect: () => void;
+ handleSubmit: () => Promise;
+ handleClose: () => void;
+ setActiveView: (view: ActiveViewEnum) => void;
+ allRegions: string[];
+ accountId?: string;
+ selectedDeploymentRegion: string | undefined;
+ handleRegionChange: (value: string) => void;
+}
+
+export function useIntegrationModal({
+ onClose,
+}: UseIntegrationModalProps): UseIntegrationModal {
+ const [form] = Form.useForm();
+ const [modalState, setModalState] = useState(
+ ModalStateEnum.FORM,
+ );
+ const [isLoading, setIsLoading] = useState(false);
+ const [accountId, setAccountId] = useState(undefined);
+ const [activeView, setActiveView] = useState(
+ ActiveViewEnum.FORM,
+ );
+ const [selectedRegions, setSelectedRegions] = useState([]);
+ const [includeAllRegions, setIncludeAllRegions] = useState(false);
+ const [selectedDeploymentRegion, setSelectedDeploymentRegion] = useState<
+ string | undefined
+ >(undefined);
+ const allRegions = useMemo(
+ () => regions.flatMap((r) => r.subRegions.map((sr) => sr.name)),
+ [],
+ );
+
+ useEffect(() => {
+ form.setFieldsValue({ region: selectedDeploymentRegion });
+ }, [selectedDeploymentRegion, form]);
+
+ const handleRegionChange = (value: string): void => {
+ setSelectedDeploymentRegion(value);
+ };
+
+ const handleIncludeAllRegionsChange = useCallback((checked: boolean): void => {
+ setIncludeAllRegions(checked);
+ if (checked) {
+ setSelectedRegions(['all']);
+ } else {
+ setSelectedRegions([]);
+ }
+ }, []);
+
+ const handleRegionSelect = useCallback((): void => {
+ setActiveView(ActiveViewEnum.SELECT_REGIONS);
+ }, []);
+
+ const handleClose = useCallback((): void => {
+ setActiveView(ActiveViewEnum.FORM);
+ setSelectedRegions([]);
+ setIncludeAllRegions(false);
+ setModalState(ModalStateEnum.FORM);
+ onClose();
+ }, [onClose]);
+
+ const {
+ mutate: generateUrl,
+ isLoading: isGeneratingUrl,
+ } = useGenerateConnectionUrl();
+
+ const handleGenerateUrl = useCallback(
+ (payload: GenerateConnectionUrlPayload): void => {
+ generateUrl(payload, {
+ onSuccess: (data: ConnectionUrlResponse) => {
+ window.open(data.connection_url, '_blank');
+ setModalState(ModalStateEnum.WAITING);
+ setAccountId(data.account_id);
+ },
+ onError: () => {
+ setModalState(ModalStateEnum.ERROR);
+ },
+ });
+ },
+ [generateUrl],
+ );
+
+ const handleSubmit = useCallback(async (): Promise => {
+ try {
+ setIsLoading(true);
+ const values = await form.validateFields();
+
+ const payload: GenerateConnectionUrlPayload = {
+ agent_config: {
+ region: values.region,
+ },
+ account_config: {
+ regions: includeAllRegions ? ['all'] : selectedRegions,
+ },
+ };
+
+ handleGenerateUrl(payload);
+ } catch (error) {
+ console.error('Form submission failed:', error);
+ } finally {
+ setIsLoading(false);
+ }
+ }, [form, includeAllRegions, selectedRegions, handleGenerateUrl]);
+
+ return {
+ form,
+ modalState,
+ isLoading,
+ activeView,
+ selectedRegions,
+ includeAllRegions,
+ isGeneratingUrl,
+ setSelectedRegions,
+ setIncludeAllRegions,
+ handleIncludeAllRegionsChange,
+ handleRegionSelect,
+ handleSubmit,
+ handleClose,
+ setActiveView,
+ allRegions,
+ accountId,
+ setModalState,
+ selectedDeploymentRegion,
+ handleRegionChange,
+ };
+}
diff --git a/frontend/src/hooks/integrations/aws/useRegionSelection.ts b/frontend/src/hooks/integrations/aws/useRegionSelection.ts
new file mode 100644
index 00000000000..fa218a7ec80
--- /dev/null
+++ b/frontend/src/hooks/integrations/aws/useRegionSelection.ts
@@ -0,0 +1,58 @@
+import { regions } from 'container/CloudIntegrationPage/ServicesSection/data';
+import { Dispatch, SetStateAction, useMemo } from 'react';
+
+interface UseRegionSelectionProps {
+ selectedRegions: string[];
+ setSelectedRegions: Dispatch>;
+ setIncludeAllRegions: Dispatch>;
+}
+
+interface UseRegionSelection {
+ allRegionIds: string[];
+ handleSelectAll: (checked: boolean) => void;
+ handleRegionSelect: (regionId: string) => void;
+}
+
+export function useRegionSelection({
+ selectedRegions,
+ setSelectedRegions,
+ setIncludeAllRegions,
+}: UseRegionSelectionProps): UseRegionSelection {
+ const allRegionIds = useMemo(
+ () => regions.flatMap((r) => r.subRegions.map((sr) => sr.id)),
+ [],
+ );
+ const handleSelectAll = (checked: boolean): void => {
+ setIncludeAllRegions(checked);
+ setSelectedRegions(checked ? ['all'] : []);
+ };
+
+ const handleRegionSelect = (regionId: string): void => {
+ if (selectedRegions.includes('all')) {
+ const filteredRegionIds = allRegionIds.filter((id) => id !== regionId);
+
+ setSelectedRegions(filteredRegionIds);
+ setIncludeAllRegions(false);
+ return;
+ }
+
+ setSelectedRegions((prev) => {
+ const newSelection = prev.includes(regionId)
+ ? prev.filter((id) => id !== regionId)
+ : [...prev, regionId];
+
+ if (newSelection.length === allRegionIds.length) {
+ setIncludeAllRegions(true);
+ return ['all'];
+ }
+
+ return newSelection;
+ });
+ };
+
+ return {
+ allRegionIds,
+ handleSelectAll,
+ handleRegionSelect,
+ };
+}
diff --git a/frontend/src/hooks/integrations/aws/useServiceDetails.ts b/frontend/src/hooks/integrations/aws/useServiceDetails.ts
new file mode 100644
index 00000000000..7e304b5137e
--- /dev/null
+++ b/frontend/src/hooks/integrations/aws/useServiceDetails.ts
@@ -0,0 +1,17 @@
+import { getServiceDetails } from 'api/integrations/aws';
+import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
+import { ServiceData } from 'container/CloudIntegrationPage/ServicesSection/types';
+import { useQuery, UseQueryResult } from 'react-query';
+
+export function useServiceDetails(
+ serviceId: string,
+ accountId?: string,
+): UseQueryResult {
+ return useQuery(
+ [REACT_QUERY_KEY.AWS_SERVICE_DETAILS, serviceId, accountId],
+ () => getServiceDetails(serviceId, accountId),
+ {
+ enabled: !!serviceId,
+ },
+ );
+}
diff --git a/frontend/src/hooks/integrations/aws/useUpdateAccountConfig.ts b/frontend/src/hooks/integrations/aws/useUpdateAccountConfig.ts
new file mode 100644
index 00000000000..fcf4da15178
--- /dev/null
+++ b/frontend/src/hooks/integrations/aws/useUpdateAccountConfig.ts
@@ -0,0 +1,22 @@
+import { updateAccountConfig } from 'api/integrations/aws';
+import { useMutation, UseMutationResult } from 'react-query';
+import {
+ AccountConfigPayload,
+ AccountConfigResponse,
+} from 'types/api/integrations/aws';
+
+interface UpdateConfigVariables {
+ accountId: string;
+ payload: AccountConfigPayload;
+}
+
+export function useUpdateAccountConfig(): UseMutationResult<
+ AccountConfigResponse,
+ Error,
+ UpdateConfigVariables
+> {
+ return useMutation({
+ mutationFn: ({ accountId, payload }) =>
+ updateAccountConfig(accountId, payload),
+ });
+}
diff --git a/frontend/src/hooks/integrations/aws/useUpdateServiceConfig.ts b/frontend/src/hooks/integrations/aws/useUpdateServiceConfig.ts
new file mode 100644
index 00000000000..17f1ce72662
--- /dev/null
+++ b/frontend/src/hooks/integrations/aws/useUpdateServiceConfig.ts
@@ -0,0 +1,45 @@
+import { updateServiceConfig } from 'api/integrations/aws';
+import { useMutation, UseMutationResult } from 'react-query';
+
+interface UpdateServiceConfigPayload {
+ cloud_account_id: string;
+ config: {
+ logs: {
+ enabled: boolean;
+ };
+ metrics: {
+ enabled: boolean;
+ };
+ };
+}
+
+interface UpdateConfigResponse {
+ status: string;
+ data: {
+ id: string;
+ config: {
+ logs: {
+ enabled: boolean;
+ };
+ metrics: {
+ enabled: boolean;
+ };
+ };
+ };
+}
+
+interface UpdateConfigVariables {
+ serviceId: string;
+ payload: UpdateServiceConfigPayload;
+}
+
+export function useUpdateServiceConfig(): UseMutationResult<
+ UpdateConfigResponse,
+ Error,
+ UpdateConfigVariables
+> {
+ return useMutation({
+ mutationFn: ({ serviceId, payload }) =>
+ updateServiceConfig(serviceId, payload),
+ });
+}
diff --git a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailHeader.tsx b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailHeader.tsx
index 97ac3e11c80..116fd525535 100644
--- a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailHeader.tsx
+++ b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailHeader.tsx
@@ -22,7 +22,7 @@ interface IntegrationDetailHeaderProps {
title: string;
description: string;
icon: string;
- refetchIntegrationDetails: () => void;
+ onUnInstallSuccess: () => void;
connectionState: ConnectionStates;
connectionData: IntegrationConnectionStatus;
setActiveDetailTab: React.Dispatch>;
@@ -38,7 +38,7 @@ function IntegrationDetailHeader(
description,
connectionState,
connectionData,
- refetchIntegrationDetails,
+ onUnInstallSuccess,
setActiveDetailTab,
} = props;
const [isModalOpen, setIsModalOpen] = useState(false);
@@ -61,7 +61,7 @@ function IntegrationDetailHeader(
installIntegration,
{
onSuccess: () => {
- refetchIntegrationDetails();
+ onUnInstallSuccess();
},
onError: () => {
notifications.error({
diff --git a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.styles.scss b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.styles.scss
index 96b5ab4c16e..823c05faf17 100644
--- a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.styles.scss
+++ b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.styles.scss
@@ -300,13 +300,13 @@
.uninstall-integration-btn {
border-radius: 2px;
- background: var(--Accent---Secondary-Cherry, #da5565);
- border-color: unset !important;
+ background: var(--bg-cherry-500);
+ border: none !important;
padding: 9px 13px;
display: flex;
align-items: center;
justify-content: center;
- color: var(--bg-ink-300);
+ color: var(--bg-vanilla-100);
text-align: center;
font-family: Inter;
font-size: 12px;
@@ -317,7 +317,7 @@
.uninstall-integration-btn:hover {
&.ant-btn-default {
- color: var(--bg-ink-300) !important;
+ color: var(--bg-vanilla-100) !important;
}
}
}
diff --git a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.tsx b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.tsx
index 9d3fdf910f5..7275c17faa7 100644
--- a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.tsx
+++ b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.tsx
@@ -126,7 +126,7 @@ function IntegrationDetailPage(props: IntegrationDetailPageProps): JSX.Element {
logs: null,
metrics: null,
})}
- refetchIntegrationDetails={refetch}
+ onUnInstallSuccess={refetch}
setActiveDetailTab={setActiveDetailTab}
/>
)}
diff --git a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationsUninstallBar.tsx b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationsUninstallBar.tsx
index 557327a19b6..547f7235e1d 100644
--- a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationsUninstallBar.tsx
+++ b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationsUninstallBar.tsx
@@ -15,8 +15,9 @@ import { ConnectionStates } from './TestConnection';
interface IntergrationsUninstallBarProps {
integrationTitle: string;
integrationId: string;
- refetchIntegrationDetails: () => void;
+ onUnInstallSuccess: () => void;
connectionStatus: ConnectionStates;
+ removeIntegrationTitle?: string;
}
function IntergrationsUninstallBar(
props: IntergrationsUninstallBarProps,
@@ -24,8 +25,9 @@ function IntergrationsUninstallBar(
const {
integrationTitle,
integrationId,
- refetchIntegrationDetails,
+ onUnInstallSuccess,
connectionStatus,
+ removeIntegrationTitle = 'Remove from SigNoz',
} = props;
const { notifications } = useNotifications();
const [isModalOpen, setIsModalOpen] = useState(false);
@@ -35,7 +37,7 @@ function IntergrationsUninstallBar(
isLoading: isUninstallLoading,
} = useMutation(unInstallIntegration, {
onSuccess: () => {
- refetchIntegrationDetails();
+ onUnInstallSuccess?.();
setIsModalOpen(false);
},
onError: () => {
@@ -79,7 +81,7 @@ function IntergrationsUninstallBar(
icon={ }
onClick={(): void => showModal()}
>
- Remove from SigNoz
+ {removeIntegrationTitle}