diff --git a/frontend/webapp/app/(overview)/overview/page.tsx b/frontend/webapp/app/(overview)/overview/page.tsx
index c24075f34..5d971be73 100644
--- a/frontend/webapp/app/(overview)/overview/page.tsx
+++ b/frontend/webapp/app/(overview)/overview/page.tsx
@@ -1,6 +1,7 @@
'use client';
import React from 'react';
import dynamic from 'next/dynamic';
+import { useSSE } from '@/hooks';
const ToastList = dynamic(() => import('@/components/notification/toast-list'), { ssr: false });
const AllDrawers = dynamic(() => import('@/components/overview/all-drawers'), { ssr: false });
@@ -8,6 +9,8 @@ const AllModals = dynamic(() => import('@/components/overview/all-modals'), { ss
const OverviewDataFlowContainer = dynamic(() => import('@/containers/main/overview/overview-data-flow'), { ssr: false });
export default function MainPage() {
+ useSSE();
+
return (
<>
diff --git a/frontend/webapp/app/layout.tsx b/frontend/webapp/app/layout.tsx
index f0254a8b5..a662b286e 100644
--- a/frontend/webapp/app/layout.tsx
+++ b/frontend/webapp/app/layout.tsx
@@ -1,7 +1,6 @@
'use client';
import './globals.css';
import React from 'react';
-import { useSSE } from '@/hooks';
import { METADATA } from '@/utils';
import { ApolloWrapper } from '@/lib';
import { ThemeProviderWrapper } from '@/styles';
@@ -15,8 +14,6 @@ const LAYOUT_STYLE: React.CSSProperties = {
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
- useSSE();
-
return (
diff --git a/frontend/webapp/app/page.tsx b/frontend/webapp/app/page.tsx
index de97cf7be..9df649d3c 100644
--- a/frontend/webapp/app/page.tsx
+++ b/frontend/webapp/app/page.tsx
@@ -1,20 +1,22 @@
'use client';
import { useEffect } from 'react';
+import { useConfig } from '@/hooks';
+import { CenterThis } from '@/styles';
+import { ROUTES, CONFIG } from '@/utils';
+import { NOTIFICATION_TYPE } from '@/types';
import { useRouter } from 'next/navigation';
-import { useNotify, useConfig } from '@/hooks';
+import { useNotificationStore } from '@/store';
import { FadeLoader } from '@/reuseable-components';
-import { ROUTES, CONFIG, NOTIFICATION } from '@/utils';
-import { CenterThis } from '@/styles';
export default function App() {
const router = useRouter();
- const notify = useNotify();
const { data, error } = useConfig();
+ const { addNotification } = useNotificationStore();
useEffect(() => {
if (error) {
- notify({
- type: NOTIFICATION.ERROR,
+ addNotification({
+ type: NOTIFICATION_TYPE.ERROR,
title: error.name,
message: error.message,
});
diff --git a/frontend/webapp/components/modals/cancel-warning/index.tsx b/frontend/webapp/components/modals/cancel-warning/index.tsx
index 82221bc9f..5e4b760d1 100644
--- a/frontend/webapp/components/modals/cancel-warning/index.tsx
+++ b/frontend/webapp/components/modals/cancel-warning/index.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import { NOTIFICATION_TYPE } from '@/types';
import { WarningModal } from '@/reuseable-components';
interface Props {
@@ -18,7 +19,7 @@ const CancelWarning: React.FC = ({ isOpen, noOverlay, name, onApprove, on
description='Are you sure you want to cancel?'
approveButton={{
text: 'Confirm',
- variant: 'warning',
+ variant: NOTIFICATION_TYPE.WARNING,
onClick: onApprove,
}}
denyButton={{
diff --git a/frontend/webapp/components/modals/delete-warning/index.tsx b/frontend/webapp/components/modals/delete-warning/index.tsx
index 01b7978c3..e59fa881a 100644
--- a/frontend/webapp/components/modals/delete-warning/index.tsx
+++ b/frontend/webapp/components/modals/delete-warning/index.tsx
@@ -1,6 +1,6 @@
import React from 'react';
-import { OVERVIEW_ENTITY_TYPES } from '@/types';
import { WarningModal } from '@/reuseable-components';
+import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES } from '@/types';
interface Props {
isOpen: boolean;
@@ -24,7 +24,7 @@ const DeleteWarning: React.FC = ({ isOpen, noOverlay, name, type, isLastI
note={
isLastItem
? {
- type: 'warning',
+ type: NOTIFICATION_TYPE.WARNING,
title: `You're about to ${actionText} the last ${type}`,
message: 'This will break your pipeline!',
}
diff --git a/frontend/webapp/components/notification/notification-manager.tsx b/frontend/webapp/components/notification/notification-manager.tsx
index 36586c700..c0c752bfe 100644
--- a/frontend/webapp/components/notification/notification-manager.tsx
+++ b/frontend/webapp/components/notification/notification-manager.tsx
@@ -6,7 +6,7 @@ import { useNotificationStore } from '@/store';
import { ACTION, getStatusIcon } from '@/utils';
import { useOnClickOutside, useTimeAgo } from '@/hooks';
import theme, { hexPercentValues } from '@/styles/theme';
-import type { Notification, NotificationType } from '@/types';
+import { NOTIFICATION_TYPE, type Notification } from '@/types';
import { IconButton, NoDataFound, Text } from '@/reuseable-components';
const RelativeContainer = styled.div`
@@ -82,14 +82,14 @@ export const NotificationManager = () => {
return (
-
+
{isOpen && (
-
+
- Notifications{' '}
+ Notifications
{!!unseenCount && (
{unseenCount} new
@@ -126,7 +126,7 @@ const NotifCard = styled.div`
}
`;
-const StatusIcon = styled.div<{ $type: NotificationType }>`
+const StatusIcon = styled.div<{ $type: NOTIFICATION_TYPE }>`
background-color: ${({ $type, theme }) => theme.text[$type] + hexPercentValues['012']};
border-radius: 8px;
width: 36px;
@@ -175,7 +175,7 @@ const NotificationListItem: React.FC void }> = (
}
}}
>
-
+
diff --git a/frontend/webapp/components/overview/add-entity/index.tsx b/frontend/webapp/components/overview/add-entity/index.tsx
index b9c449995..5323720ee 100644
--- a/frontend/webapp/components/overview/add-entity/index.tsx
+++ b/frontend/webapp/components/overview/add-entity/index.tsx
@@ -1,11 +1,11 @@
+import React, { useState, useRef } from 'react';
import Image from 'next/image';
import theme from '@/styles/theme';
-import { useOnClickOutside } from '@/hooks';
-import React, { useState, useRef } from 'react';
+import { useModalStore } from '@/store';
import styled, { css } from 'styled-components';
-import { useBooleanStore, useModalStore } from '@/store';
-import { DropdownOption, OVERVIEW_ENTITY_TYPES } from '@/types';
+import { useComputePlatform, useOnClickOutside } from '@/hooks';
import { Button, FadeLoader, Text } from '@/reuseable-components';
+import { type DropdownOption, OVERVIEW_ENTITY_TYPES } from '@/types';
// Styled components for the dropdown UI
const Container = styled.div`
@@ -65,13 +65,13 @@ const DEFAULT_OPTIONS: DropdownOption[] = [
{ id: OVERVIEW_ENTITY_TYPES.DESTINATION, value: 'Destination' },
];
-interface AddEntityButtonDropdownProps {
+interface Props {
options?: DropdownOption[];
placeholder?: string;
}
-const AddEntity: React.FC = ({ options = DEFAULT_OPTIONS, placeholder = 'ADD...' }) => {
- const { isPolling } = useBooleanStore();
+const AddEntity: React.FC = ({ options = DEFAULT_OPTIONS, placeholder = 'ADD...' }) => {
+ const { loading } = useComputePlatform();
const { currentModal, setCurrentModal } = useModalStore();
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
@@ -91,7 +91,7 @@ const AddEntity: React.FC = ({ options = DEFAULT_O
return (
- {isPolling ? : }
+ {loading ? : }
{placeholder}
diff --git a/frontend/webapp/containers/main/destinations/add-destination/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/index.tsx
index b4bc07f54..0e293f753 100644
--- a/frontend/webapp/containers/main/destinations/add-destination/index.tsx
+++ b/frontend/webapp/containers/main/destinations/add-destination/index.tsx
@@ -2,15 +2,16 @@ import React, { useState } from 'react';
import Image from 'next/image';
import { ROUTES } from '@/utils';
import theme from '@/styles/theme';
+import { CenterThis } from '@/styles';
import { useAppStore } from '@/store';
import styled from 'styled-components';
import { SetupHeader } from '@/components';
import { useRouter } from 'next/navigation';
-import { useDestinationCRUD, useSourceCRUD } from '@/hooks';
+import { NOTIFICATION_TYPE } from '@/types';
import { DestinationModal } from '../destination-modal';
+import { useDestinationCRUD, useSourceCRUD } from '@/hooks';
import { ConfiguredDestinationsList } from './configured-destinations-list';
import { Button, FadeLoader, NotificationNote, SectionTitle, Text } from '@/reuseable-components';
-import { CenterThis } from '@/styles';
const ContentWrapper = styled.div`
width: 640px;
@@ -93,7 +94,7 @@ export function AddDestinationContainer() {
{!isLoading && isSourcesListEmpty() && (
();
+ const [connectionStatus, setConnectionStatus] = useState();
const dirtyForm = () => {
setIsFormDirty(true);
@@ -87,11 +87,11 @@ export function DestinationFormBody({ isUpdate, destination, formData, formError
status={connectionStatus}
onError={() => {
setIsFormDirty(false);
- setConnectionStatus('error');
+ setConnectionStatus(NOTIFICATION_TYPE.ERROR);
}}
onSuccess={() => {
setIsFormDirty(false);
- setConnectionStatus('success');
+ setConnectionStatus(NOTIFICATION_TYPE.SUCCESS);
}}
validateForm={validateForm}
/>
@@ -101,9 +101,9 @@ export function DestinationFormBody({ isUpdate, destination, formData, formError
{testConnectionSupported && (
- {connectionStatus === 'error' && }
- {connectionStatus === 'success' && }
- {!connectionStatus && }
+ {connectionStatus === NOTIFICATION_TYPE.ERROR && }
+ {connectionStatus === NOTIFICATION_TYPE.SUCCESS && }
+ {!connectionStatus && }
)}
diff --git a/frontend/webapp/containers/main/destinations/destination-form-body/test-connection/index.tsx b/frontend/webapp/containers/main/destinations/destination-form-body/test-connection/index.tsx
index 15af5b783..a08ad2ac4 100644
--- a/frontend/webapp/containers/main/destinations/destination-form-body/test-connection/index.tsx
+++ b/frontend/webapp/containers/main/destinations/destination-form-body/test-connection/index.tsx
@@ -3,22 +3,22 @@ import Image from 'next/image';
import theme from '@/styles/theme';
import { getStatusIcon } from '@/utils';
import { useTestConnection } from '@/hooks';
-import type { DestinationInput } from '@/types';
import styled, { css } from 'styled-components';
import { Button, FadeLoader, Text } from '@/reuseable-components';
+import { type DestinationInput, NOTIFICATION_TYPE } from '@/types';
-type Status = 'success' | 'error';
+export type ConnectionStatus = NOTIFICATION_TYPE.SUCCESS | NOTIFICATION_TYPE.ERROR;
interface Props {
destination: DestinationInput;
disabled: boolean;
- status?: Status;
+ status?: ConnectionStatus;
onError: () => void;
onSuccess: () => void;
validateForm: () => boolean;
}
-const ActionButton = styled(Button)<{ $status?: Status }>`
+const ActionButton = styled(Button)<{ $status?: ConnectionStatus }>`
display: flex;
align-items: center;
gap: 8px;
diff --git a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx
index 21ca7e8c8..04b6e5f4e 100644
--- a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx
+++ b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx
@@ -2,14 +2,14 @@ import React, { useMemo, useState } from 'react';
import buildCard from './build-card';
import { RuleFormBody } from '../';
import styled from 'styled-components';
-import { useDrawerStore } from '@/store';
import { DataCard } from '@/reuseable-components';
import buildDrawerItem from './build-drawer-item';
import { RULE_OPTIONS } from '../rule-modal/rule-options';
import OverviewDrawer from '../../overview/overview-drawer';
-import { ACTION, DATA_CARDS, FORM_ALERTS, getRuleIcon, NOTIFICATION } from '@/utils';
-import { useInstrumentationRuleCRUD, useInstrumentationRuleFormData, useNotify } from '@/hooks';
-import { InstrumentationRuleType, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleSpec } from '@/types';
+import { useDrawerStore, useNotificationStore } from '@/store';
+import { ACTION, DATA_CARDS, FORM_ALERTS, getRuleIcon } from '@/utils';
+import { useInstrumentationRuleCRUD, useInstrumentationRuleFormData } from '@/hooks';
+import { InstrumentationRuleType, NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleSpec } from '@/types';
interface Props {}
@@ -22,7 +22,7 @@ const FormContainer = styled.div`
`;
export const RuleDrawer: React.FC = () => {
- const notify = useNotify();
+ const { addNotification } = useNotificationStore();
const { selectedItem, setSelectedItem } = useDrawerStore();
const { formData, formErrors, handleFormChange, resetFormData, validateForm, loadFormWithDrawerItem } = useInstrumentationRuleFormData();
@@ -74,7 +74,7 @@ export const RuleDrawer: React.FC = () => {
const handleEdit = (bool?: boolean) => {
if (item.type === InstrumentationRuleType.UNKNOWN_TYPE) {
- notify({ type: NOTIFICATION.WARNING, title: FORM_ALERTS.FORBIDDEN, message: FORM_ALERTS.CANNOT_EDIT_RULE, crdType: OVERVIEW_ENTITY_TYPES.RULE, target: id });
+ addNotification({ type: NOTIFICATION_TYPE.WARNING, title: FORM_ALERTS.FORBIDDEN, message: FORM_ALERTS.CANNOT_EDIT_RULE, crdType: OVERVIEW_ENTITY_TYPES.RULE, target: id });
} else {
setIsEditing(typeof bool === 'boolean' ? bool : true);
}
diff --git a/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx b/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx
index f1b6f52cf..554fda070 100644
--- a/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx
+++ b/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { ACTION } from '@/utils';
import { RuleFormBody } from '../';
+import { NOTIFICATION_TYPE } from '@/types';
import { CenterThis, ModalBody } from '@/styles';
import { RULE_OPTIONS, RuleOption } from './rule-options';
import { useInstrumentationRuleCRUD, useInstrumentationRuleFormData, useKeyDown } from '@/hooks';
@@ -58,7 +59,7 @@ export const RuleModal: React.FC = ({ isOpen, onClose }) => {
>
-
+
{!!selectedItem?.type ? (
diff --git a/frontend/webapp/containers/main/overview/overview-data-flow/index.tsx b/frontend/webapp/containers/main/overview/overview-data-flow/index.tsx
index c834b2fb3..e4198e67c 100644
--- a/frontend/webapp/containers/main/overview/overview-data-flow/index.tsx
+++ b/frontend/webapp/containers/main/overview/overview-data-flow/index.tsx
@@ -35,15 +35,9 @@ export default function OverviewDataFlowContainer() {
const positions = useMemo(() => getNodePositions({ containerWidth }), [containerWidth]);
const { metrics } = useMetrics();
- const { data, filteredData, startPolling } = useComputePlatform();
+ const { data, filteredData } = useComputePlatform();
const unfilteredCounts = useMemo(() => getEntityCounts({ computePlatform: data?.computePlatform }), [data]);
- useEffect(() => {
- // this is to start polling on component mount in an attempt to fix any initial errors with sources/destinations
- if (!!data?.computePlatform.k8sActualSources.length || !!data?.computePlatform.destinations.length) startPolling();
- // only on-mount, if we include "data" this will trigger on every refetch, causing an infinite loop
- }, []);
-
const ruleNodes = useMemo(
() => buildRuleNodes({ entities: filteredData?.computePlatform.instrumentationRules || [], positions, unfilteredCounts }),
[filteredData?.computePlatform.instrumentationRules, positions, unfilteredCounts],
diff --git a/frontend/webapp/cypress/constants/index.ts b/frontend/webapp/cypress/constants/index.ts
index 8afbf4d6e..fcc22c8b9 100644
--- a/frontend/webapp/cypress/constants/index.ts
+++ b/frontend/webapp/cypress/constants/index.ts
@@ -70,6 +70,9 @@ export const DATA_IDS = {
TITLE: '[data-id=title]',
SOURCE_TITLE: '[data-id=sourceName]',
CHECKBOX: '[data-id=checkbox]',
+
+ NOTIF_MANAGER_BUTTON: '[data-id=notif-manager-button]',
+ NOTIF_MANAGER_CONTENR: '[data-id=notif-manager-content]',
};
export const BUTTONS = {
@@ -88,6 +91,8 @@ const CYPRESS_TEST = 'Cypress Test';
export const TEXTS = {
UPDATED_NAME: CYPRESS_TEST,
+ NO_RESOURCES: (namespace: string) => `No resources found in ${namespace} namespace.`,
+
SOURCE_WARN_MODAL_TITLE: 'Uninstrument 5 sources',
SOURCE_WARN_MODAL_NOTE: "You're about to uninstrument the last source",
DESTINATION_WARN_MODAL_TITLE: `Delete destination (${CYPRESS_TEST})`,
@@ -95,5 +100,6 @@ export const TEXTS = {
ACTION_WARN_MODAL_TITLE: `Delete action (${CYPRESS_TEST})`,
INSTRUMENTATION_RULE_WARN_MODAL_TITLE: `Delete rule (${CYPRESS_TEST})`,
- NO_RESOURCES: (namespace: string) => `No resources found in ${namespace} namespace.`,
+ NOTIF_SOURCES_CREATED: 'successfully added 5 sources',
+ NOTIF_SOURCES_DELETED: 'successfully deleted 5 sources',
};
diff --git a/frontend/webapp/cypress/e2e/03-sources.cy.ts b/frontend/webapp/cypress/e2e/03-sources.cy.ts
index 59f69e44b..84d3709e9 100644
--- a/frontend/webapp/cypress/e2e/03-sources.cy.ts
+++ b/frontend/webapp/cypress/e2e/03-sources.cy.ts
@@ -11,7 +11,7 @@ const crdName = CRD_NAMES.SOURCE;
describe('Sources CRUD', () => {
beforeEach(() => cy.intercept('/graphql').as('gql'));
- it('Should create a CRD in the cluster', () => {
+ it('Should create a CRD in the cluster, and notify with SSE', () => {
cy.visit(ROUTES.OVERVIEW);
getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }, () => {
@@ -25,7 +25,10 @@ describe('Sources CRUD', () => {
cy.contains('button', BUTTONS.DONE).click();
cy.wait('@gql').then(() => {
- getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 });
+ getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }, () => {
+ cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click();
+ cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist');
+ });
});
});
});
@@ -55,7 +58,7 @@ describe('Sources CRUD', () => {
});
});
- it('Should delete the CRD from the cluster', () => {
+ it('Should delete the CRD from the cluster, and notify with SSE', () => {
cy.visit(ROUTES.OVERVIEW);
getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }, () => {
@@ -66,7 +69,10 @@ describe('Sources CRUD', () => {
cy.get(DATA_IDS.APPROVE).click();
cy.wait('@gql').then(() => {
- getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 });
+ getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }, () => {
+ cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click();
+ cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist');
+ });
});
});
});
diff --git a/frontend/webapp/cypress/e2e/04-destinations.cy.ts b/frontend/webapp/cypress/e2e/04-destinations.cy.ts
index bc4ec28f3..a510d03c7 100644
--- a/frontend/webapp/cypress/e2e/04-destinations.cy.ts
+++ b/frontend/webapp/cypress/e2e/04-destinations.cy.ts
@@ -71,4 +71,3 @@ describe('Destinations CRUD', () => {
});
});
});
-// destination-odigos.io.dest.jaeger-chc52
diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts
index f75ba691a..f261ee47e 100644
--- a/frontend/webapp/hooks/actions/useActionCRUD.ts
+++ b/frontend/webapp/hooks/actions/useActionCRUD.ts
@@ -1,10 +1,9 @@
import { useMutation } from '@apollo/client';
import { useNotificationStore } from '@/store';
-import { useNotify } from '../notification/useNotify';
+import { ACTION, getSseTargetFromId } from '@/utils';
import { useComputePlatform } from '../compute-platform';
-import { ACTION, getSseTargetFromId, NOTIFICATION } from '@/utils';
import { CREATE_ACTION, DELETE_ACTION, UPDATE_ACTION } from '@/graphql/mutations';
-import { OVERVIEW_ENTITY_TYPES, type ActionInput, type ActionsType, type NotificationType } from '@/types';
+import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type ActionInput, type ActionsType } from '@/types';
interface UseActionCrudParams {
onSuccess?: (type: string) => void;
@@ -14,10 +13,10 @@ interface UseActionCrudParams {
export const useActionCRUD = (params?: UseActionCrudParams) => {
const removeNotifications = useNotificationStore((store) => store.removeNotifications);
const { data, refetch } = useComputePlatform();
- const notify = useNotify();
+ const { addNotification } = useNotificationStore();
- const notifyUser = (type: NotificationType, title: string, message: string, id?: string) => {
- notify({
+ const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => {
+ addNotification({
type,
title,
message,
@@ -27,12 +26,12 @@ export const useActionCRUD = (params?: UseActionCrudParams) => {
};
const handleError = (title: string, message: string, id?: string) => {
- notifyUser(NOTIFICATION.ERROR, title, message, id);
+ notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id);
params?.onError?.(title);
};
const handleComplete = (title: string, message: string, id?: string) => {
- notifyUser(NOTIFICATION.SUCCESS, title, message, id);
+ notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id);
refetch();
params?.onSuccess?.(title);
};
diff --git a/frontend/webapp/hooks/actions/useActionFormData.ts b/frontend/webapp/hooks/actions/useActionFormData.ts
index 03d0ee864..61872fdb8 100644
--- a/frontend/webapp/hooks/actions/useActionFormData.ts
+++ b/frontend/webapp/hooks/actions/useActionFormData.ts
@@ -1,7 +1,7 @@
-import { DrawerItem } from '@/store';
-import { useGenericForm, useNotify } from '@/hooks';
-import { FORM_ALERTS, NOTIFICATION } from '@/utils';
-import type { ActionDataParsed, ActionInput } from '@/types';
+import { DrawerItem, useNotificationStore } from '@/store';
+import { FORM_ALERTS } from '@/utils';
+import { useGenericForm } from '@/hooks';
+import { NOTIFICATION_TYPE, type ActionDataParsed, type ActionInput } from '@/types';
const INITIAL: ActionInput = {
// @ts-ignore (TS complains about empty string because we expect an "ActionsType", but it's fine)
@@ -14,7 +14,7 @@ const INITIAL: ActionInput = {
};
export function useActionFormData() {
- const notify = useNotify();
+ const { addNotification } = useNotificationStore();
const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm(INITIAL);
const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => {
@@ -38,8 +38,8 @@ export function useActionFormData() {
});
if (!ok && params?.withAlert) {
- notify({
- type: NOTIFICATION.WARNING,
+ addNotification({
+ type: NOTIFICATION_TYPE.WARNING,
title: params.alertTitle,
message: FORM_ALERTS.REQUIRED_FIELDS,
});
diff --git a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts
index f3835e368..919c032aa 100644
--- a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts
+++ b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts
@@ -1,6 +1,5 @@
-import { useCallback, useMemo } from 'react';
+import { useMemo } from 'react';
import { useQuery } from '@apollo/client';
-import { useBooleanStore } from '@/store';
import { GET_COMPUTE_PLATFORM } from '@/graphql';
import { useFilterStore } from '@/store/useFilterStore';
import { BACKEND_BOOLEAN, deriveTypeFromRule, safeJsonParse } from '@/utils';
@@ -12,30 +11,12 @@ type UseComputePlatformHook = {
loading: boolean;
error?: Error;
refetch: () => void;
- startPolling: () => Promise;
};
export const useComputePlatform = (): UseComputePlatformHook => {
const { data, loading, error, refetch } = useQuery(GET_COMPUTE_PLATFORM);
- const { togglePolling } = useBooleanStore();
const filters = useFilterStore();
- const startPolling = useCallback(async () => {
- togglePolling(true);
-
- let retries = 0;
- const maxRetries = 5;
- const retryInterval = 3 * 1000; // time in milliseconds
-
- while (retries < maxRetries) {
- await new Promise((resolve) => setTimeout(resolve, retryInterval));
- refetch();
- retries++;
- }
-
- togglePolling(false);
- }, [refetch, togglePolling]);
-
const mappedData = useMemo(() => {
if (!data) return undefined;
@@ -95,5 +76,5 @@ export const useComputePlatform = (): UseComputePlatformHook => {
};
}, [mappedData, filters]);
- return { data: mappedData, filteredData, loading, error, refetch, startPolling };
+ return { data: mappedData, filteredData, loading, error, refetch };
};
diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts
index 7bf034592..fe95b9ea5 100644
--- a/frontend/webapp/hooks/compute-platform/useNamespace.ts
+++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts
@@ -1,20 +1,19 @@
-import { NOTIFICATION } from '@/utils';
-import { useNotify } from '../notification';
+import { useNotificationStore } from '@/store';
import { useMutation, useQuery } from '@apollo/client';
import { useComputePlatform } from './useComputePlatform';
import { GET_NAMESPACES, PERSIST_NAMESPACE } from '@/graphql';
-import { ComputePlatform, PersistNamespaceItemInput } from '@/types';
+import { type ComputePlatform, NOTIFICATION_TYPE, type PersistNamespaceItemInput } from '@/types';
export const useNamespace = (namespaceName?: string, instrumentationLabeled = null as boolean | null) => {
- const notify = useNotify();
+ const { addNotification } = useNotificationStore();
const cp = useComputePlatform();
const handleError = (title: string, message: string) => {
- notify({ type: NOTIFICATION.ERROR, title, message });
+ addNotification({ type: NOTIFICATION_TYPE.ERROR, title, message });
};
const handleComplete = (title: string, message: string) => {
- notify({ type: NOTIFICATION.SUCCESS, title, message });
+ addNotification({ type: NOTIFICATION_TYPE.SUCCESS, title, message });
};
const { data, loading, error } = useQuery(GET_NAMESPACES, {
diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts
index 2088c18cc..f30bf80cb 100644
--- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts
+++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts
@@ -1,9 +1,8 @@
import { useMutation } from '@apollo/client';
import { useNotificationStore } from '@/store';
-import { useNotify } from '../notification/useNotify';
+import { ACTION, getSseTargetFromId } from '@/utils';
import { useComputePlatform } from '../compute-platform';
-import { ACTION, getSseTargetFromId, NOTIFICATION } from '@/utils';
-import { OVERVIEW_ENTITY_TYPES, type DestinationInput, type NotificationType } from '@/types';
+import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type DestinationInput } from '@/types';
import { CREATE_DESTINATION, DELETE_DESTINATION, UPDATE_DESTINATION } from '@/graphql/mutations';
interface Params {
@@ -14,10 +13,10 @@ interface Params {
export const useDestinationCRUD = (params?: Params) => {
const removeNotifications = useNotificationStore((store) => store.removeNotifications);
const { data, refetch } = useComputePlatform();
- const notify = useNotify();
+ const { addNotification } = useNotificationStore();
- const notifyUser = (type: NotificationType, title: string, message: string, id?: string) => {
- notify({
+ const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => {
+ addNotification({
type,
title,
message,
@@ -27,12 +26,12 @@ export const useDestinationCRUD = (params?: Params) => {
};
const handleError = (title: string, message: string, id?: string) => {
- notifyUser(NOTIFICATION.ERROR, title, message, id);
+ notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id);
params?.onError?.(title);
};
const handleComplete = (title: string, message: string, id?: string) => {
- notifyUser(NOTIFICATION.SUCCESS, title, message, id);
+ notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id);
refetch();
params?.onSuccess?.(title);
};
diff --git a/frontend/webapp/hooks/destinations/useDestinationFormData.ts b/frontend/webapp/hooks/destinations/useDestinationFormData.ts
index 07927d7e0..eb50c9ac2 100644
--- a/frontend/webapp/hooks/destinations/useDestinationFormData.ts
+++ b/frontend/webapp/hooks/destinations/useDestinationFormData.ts
@@ -1,9 +1,9 @@
import { useState, useEffect } from 'react';
-import { DrawerItem } from '@/store';
import { useQuery } from '@apollo/client';
import { GET_DESTINATION_TYPE_DETAILS } from '@/graphql';
-import { useConnectDestinationForm, useGenericForm, useNotify } from '@/hooks';
-import { ACTION, FORM_ALERTS, NOTIFICATION, safeJsonParse } from '@/utils';
+import { DrawerItem, useNotificationStore } from '@/store';
+import { ACTION, FORM_ALERTS, safeJsonParse } from '@/utils';
+import { useConnectDestinationForm, useGenericForm } from '@/hooks';
import {
type DynamicField,
type DestinationDetailsResponse,
@@ -12,6 +12,7 @@ import {
type ActualDestination,
type SupportedDestinationSignals,
OVERVIEW_ENTITY_TYPES,
+ NOTIFICATION_TYPE,
} from '@/types';
const INITIAL: DestinationInput = {
@@ -28,7 +29,7 @@ const INITIAL: DestinationInput = {
export function useDestinationFormData(params?: { destinationType?: string; supportedSignals?: SupportedDestinationSignals; preLoadedFields?: string | DestinationTypeItem['fields'] }) {
const { destinationType, supportedSignals, preLoadedFields } = params || {};
- const notify = useNotify();
+ const { addNotification } = useNotificationStore();
const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm(INITIAL);
const { buildFormDynamicFields } = useConnectDestinationForm();
@@ -38,7 +39,13 @@ export function useDestinationFormData(params?: { destinationType?: string; supp
const { data: { destinationTypeDetails } = {} } = useQuery(GET_DESTINATION_TYPE_DETAILS, {
variables: { type: t },
skip: !t,
- onError: (error) => notify({ type: NOTIFICATION.ERROR, title: ACTION.FETCH, message: error.message, crdType: OVERVIEW_ENTITY_TYPES.DESTINATION }),
+ onError: (error) =>
+ addNotification({
+ type: NOTIFICATION_TYPE.ERROR,
+ title: ACTION.FETCH,
+ message: error.message,
+ crdType: OVERVIEW_ENTITY_TYPES.DESTINATION,
+ }),
});
useEffect(() => {
@@ -98,8 +105,8 @@ export function useDestinationFormData(params?: { destinationType?: string; supp
});
if (!ok && params?.withAlert) {
- notify({
- type: NOTIFICATION.WARNING,
+ addNotification({
+ type: NOTIFICATION_TYPE.WARNING,
title: params.alertTitle,
message: FORM_ALERTS.REQUIRED_FIELDS,
});
diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts
index 3181d3cbc..407c8961b 100644
--- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts
+++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts
@@ -1,9 +1,8 @@
import { useMutation } from '@apollo/client';
import { useNotificationStore } from '@/store';
-import { useNotify } from '../notification/useNotify';
import { useComputePlatform } from '../compute-platform';
-import { ACTION, deriveTypeFromRule, getSseTargetFromId, NOTIFICATION } from '@/utils';
-import { OVERVIEW_ENTITY_TYPES, type InstrumentationRuleInput, type NotificationType } from '@/types';
+import { ACTION, deriveTypeFromRule, getSseTargetFromId } from '@/utils';
+import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleInput } from '@/types';
import { CREATE_INSTRUMENTATION_RULE, UPDATE_INSTRUMENTATION_RULE, DELETE_INSTRUMENTATION_RULE } from '@/graphql/mutations';
interface Params {
@@ -14,10 +13,10 @@ interface Params {
export const useInstrumentationRuleCRUD = (params?: Params) => {
const removeNotifications = useNotificationStore((store) => store.removeNotifications);
const { data, refetch } = useComputePlatform();
- const notify = useNotify();
+ const { addNotification } = useNotificationStore();
- const notifyUser = (type: NotificationType, title: string, message: string, id?: string) => {
- notify({
+ const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => {
+ addNotification({
type,
title,
message,
@@ -27,12 +26,12 @@ export const useInstrumentationRuleCRUD = (params?: Params) => {
};
const handleError = (title: string, message: string, id?: string) => {
- notifyUser(NOTIFICATION.ERROR, title, message, id);
+ notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id);
params?.onError?.(title);
};
const handleComplete = (title: string, message: string, id?: string) => {
- notifyUser(NOTIFICATION.SUCCESS, title, message, id);
+ notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id);
refetch();
params?.onSuccess?.(title);
};
diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts
index 6fe9b7232..20d14e7df 100644
--- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts
+++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts
@@ -1,7 +1,7 @@
-import type { DrawerItem } from '@/store';
-import { useGenericForm, useNotify } from '@/hooks';
-import { FORM_ALERTS, NOTIFICATION } from '@/utils';
-import { PayloadCollectionType, type InstrumentationRuleInput, type InstrumentationRuleSpec } from '@/types';
+import { FORM_ALERTS } from '@/utils';
+import { useGenericForm } from '@/hooks';
+import { useNotificationStore, type DrawerItem } from '@/store';
+import { NOTIFICATION_TYPE, PayloadCollectionType, type InstrumentationRuleInput, type InstrumentationRuleSpec } from '@/types';
const INITIAL: InstrumentationRuleInput = {
ruleName: '',
@@ -18,7 +18,7 @@ const INITIAL: InstrumentationRuleInput = {
};
export function useInstrumentationRuleFormData() {
- const notify = useNotify();
+ const { addNotification } = useNotificationStore();
const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm(INITIAL);
const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => {
@@ -41,8 +41,8 @@ export function useInstrumentationRuleFormData() {
});
if (!ok && params?.withAlert) {
- notify({
- type: NOTIFICATION.WARNING,
+ addNotification({
+ type: NOTIFICATION_TYPE.WARNING,
title: params.alertTitle,
message: FORM_ALERTS.REQUIRED_FIELDS,
});
diff --git a/frontend/webapp/hooks/notification/index.ts b/frontend/webapp/hooks/notification/index.ts
index 44d4bfd4e..20874d0ed 100644
--- a/frontend/webapp/hooks/notification/index.ts
+++ b/frontend/webapp/hooks/notification/index.ts
@@ -1,3 +1,2 @@
export * from './useClickNotif';
-export * from './useNotify';
export * from './useSSE';
diff --git a/frontend/webapp/hooks/notification/useNotify.ts b/frontend/webapp/hooks/notification/useNotify.ts
deleted file mode 100644
index 910ffe07a..000000000
--- a/frontend/webapp/hooks/notification/useNotify.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useNotificationStore } from '@/store';
-import { Notification } from '@/types';
-
-export const useNotify = () => {
- const { addNotification } = useNotificationStore();
-
- const notify = ({
- type,
- title,
- message,
- crdType,
- target,
- }: {
- type: Notification['type'];
- title?: Notification['title'];
- message?: Notification['message'];
- crdType?: Notification['crdType'];
- target?: Notification['target'];
- }) => {
- addNotification({ type, title, message, crdType, target });
- };
-
- return notify;
-};
diff --git a/frontend/webapp/hooks/notification/useSSE.ts b/frontend/webapp/hooks/notification/useSSE.ts
index 98f67a4b5..44629d415 100644
--- a/frontend/webapp/hooks/notification/useSSE.ts
+++ b/frontend/webapp/hooks/notification/useSSE.ts
@@ -1,13 +1,27 @@
-import { useEffect, useRef, useState } from 'react';
-import { useNotify } from './useNotify';
-import { API, NOTIFICATION } from '@/utils';
-import { useConnectionStore } from '@/store';
-
-export function useSSE() {
- const notify = useNotify();
+import { useEffect, useRef } from 'react';
+import { API } from '@/utils';
+import { NOTIFICATION_TYPE } from '@/types';
+import { useComputePlatform } from '../compute-platform';
+import { type NotifyPayload, useConnectionStore, useNotificationStore } from '@/store';
+
+const modifyType = (notification: NotifyPayload) => {
+ if (notification.title === 'Modified') {
+ if (notification.message?.indexOf('ProcessTerminated') === 0 || notification.message?.indexOf('NoHeartbeat') === 0 || notification.message?.indexOf('Failed') === 0) {
+ return NOTIFICATION_TYPE.ERROR;
+ } else {
+ return NOTIFICATION_TYPE.INFO;
+ }
+ }
+
+ return notification.type;
+};
+
+export const useSSE = () => {
+ const { addNotification } = useNotificationStore();
const { setConnectionStore } = useConnectionStore();
+ const { refetch: refetchComputePlatform } = useComputePlatform();
- const [retryCount, setRetryCount] = useState(0);
+ const retryCount = useRef(0);
const eventBuffer = useRef({});
const maxRetries = 10;
@@ -15,72 +29,61 @@ export function useSSE() {
const connect = () => {
const eventSource = new EventSource(API.EVENTS);
- eventSource.onmessage = function (event) {
- const data = JSON.parse(event.data);
+ eventSource.onmessage = (event) => {
const key = event.data;
+ const data = JSON.parse(key);
- const notification = {
- id: Date.now(),
- message: data.data,
- title: data.event,
+ const notification: NotifyPayload = {
type: data.type,
- target: data.target,
+ title: data.event,
+ message: data.data,
crdType: data.crdType,
+ target: data.target,
};
+ notification.type = modifyType(notification);
+
// Check if the event is already in the buffer
if (eventBuffer.current[key] && eventBuffer.current[key].id > Date.now() - 2000) {
eventBuffer.current[key] = notification;
- return;
} else {
// Add a new event to the buffer
eventBuffer.current[key] = notification;
- }
- // Dispatch the notification to the store
- notify({
- type: eventBuffer.current[key].type,
- title: eventBuffer.current[key].title,
- message: eventBuffer.current[key].message,
- crdType: eventBuffer.current[key].crdType,
- target: eventBuffer.current[key].target,
- });
+ // Dispatch the notification to the store
+ addNotification(notification);
+ refetchComputePlatform();
+ }
// Reset retry count on successful connection
- setRetryCount(0);
+ retryCount.current = 0;
};
- eventSource.onerror = function (event) {
+ eventSource.onerror = (event) => {
console.error('EventSource failed:', event);
eventSource.close();
// Retry connection with exponential backoff if below max retries
- setRetryCount((prevRetryCount) => {
- if (prevRetryCount < maxRetries) {
- const newRetryCount = prevRetryCount + 1;
- const retryTimeout = Math.min(10000, 1000 * Math.pow(2, newRetryCount));
-
- setTimeout(() => connect(), retryTimeout);
-
- return newRetryCount;
- } else {
- console.error('Max retries reached. Could not reconnect to EventSource.');
-
- setConnectionStore({
- connecting: false,
- active: false,
- title: `Connection lost on ${new Date().toLocaleString()}`,
- message: 'Please reboot the application',
- });
- notify({
- type: NOTIFICATION.ERROR,
- title: 'Connection Error',
- message: 'Connection to the server failed. Please reboot the application.',
- });
-
- return prevRetryCount;
- }
- });
+ if (retryCount.current < maxRetries) {
+ retryCount.current += 1;
+ const retryTimeout = Math.min(10000, 1000 * Math.pow(2, retryCount.current));
+
+ setTimeout(() => connect(), retryTimeout);
+ } else {
+ console.error('Max retries reached. Could not reconnect to EventSource.');
+
+ setConnectionStore({
+ connecting: false,
+ active: false,
+ title: `Connection lost on ${new Date().toLocaleString()}`,
+ message: 'Please reboot the application',
+ });
+ addNotification({
+ type: NOTIFICATION_TYPE.ERROR,
+ title: 'Connection Error',
+ message: 'Connection to the server failed. Please reboot the application.',
+ });
+ }
};
setConnectionStore({
@@ -100,4 +103,4 @@ export function useSSE() {
eventSource.close();
};
}, []);
-}
+};
diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts
index fc679efa7..82f30bdf7 100644
--- a/frontend/webapp/hooks/sources/useSourceCRUD.ts
+++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts
@@ -1,10 +1,9 @@
-import { useNotify } from '../notification';
import { useMutation } from '@apollo/client';
import { useNotificationStore } from '@/store';
-import { ACTION, getSseTargetFromId, NOTIFICATION } from '@/utils';
+import { ACTION, getSseTargetFromId } from '@/utils';
import { PERSIST_SOURCE, UPDATE_K8S_ACTUAL_SOURCE } from '@/graphql';
import { useComputePlatform, useNamespace } from '../compute-platform';
-import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type NotificationType, type PatchSourceRequestInput, type K8sActualSource } from '@/types';
+import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type PatchSourceRequestInput, type K8sActualSource, NOTIFICATION_TYPE } from '@/types';
interface Params {
onSuccess?: (type: string) => void;
@@ -13,12 +12,12 @@ interface Params {
export const useSourceCRUD = (params?: Params) => {
const removeNotifications = useNotificationStore((store) => store.removeNotifications);
- const { data, startPolling } = useComputePlatform();
const { persistNamespace } = useNamespace();
- const notify = useNotify();
+ const { data, refetch } = useComputePlatform();
+ const { addNotification } = useNotificationStore();
- const notifyUser = (type: NotificationType, title: string, message: string, id?: WorkloadId) => {
- notify({
+ const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId) => {
+ addNotification({
type,
title,
message,
@@ -28,13 +27,13 @@ export const useSourceCRUD = (params?: Params) => {
};
const handleError = (title: string, message: string, id?: WorkloadId) => {
- notifyUser(NOTIFICATION.ERROR, title, message, id);
+ notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id);
params?.onError?.(title);
};
const handleComplete = (title: string, message: string, id?: WorkloadId) => {
- notifyUser(NOTIFICATION.SUCCESS, title, message, id);
- startPolling();
+ notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id);
+ refetch();
params?.onSuccess?.(title);
};
diff --git a/frontend/webapp/reuseable-components/condition-details/index.tsx b/frontend/webapp/reuseable-components/condition-details/index.tsx
index 5d3baabec..392a66c06 100644
--- a/frontend/webapp/reuseable-components/condition-details/index.tsx
+++ b/frontend/webapp/reuseable-components/condition-details/index.tsx
@@ -2,8 +2,8 @@ import React, { useMemo, useState } from 'react';
import Image from 'next/image';
import theme from '@/styles/theme';
import styled from 'styled-components';
-import type { Condition } from '@/types';
import { BACKEND_BOOLEAN, getStatusIcon } from '@/utils';
+import { NOTIFICATION_TYPE, type Condition } from '@/types';
import { ExtendIcon, FadeLoader, Text } from '@/reuseable-components';
interface Props {
@@ -51,7 +51,7 @@ export const ConditionDetails: React.FC = ({ conditions }) => {
return (
setExtend((prev) => !prev)} $hasErrors={hasErrors}>
- {loading ? : }
+ {loading ? : }
{headerText}
@@ -67,7 +67,7 @@ export const ConditionDetails: React.FC = ({ conditions }) => {
{conditions.map(({ status, message }, idx) => (
-
+
{message}
diff --git a/frontend/webapp/reuseable-components/divider/index.tsx b/frontend/webapp/reuseable-components/divider/index.tsx
index 35294176b..392fbca30 100644
--- a/frontend/webapp/reuseable-components/divider/index.tsx
+++ b/frontend/webapp/reuseable-components/divider/index.tsx
@@ -1,11 +1,11 @@
import React from 'react';
import styled from 'styled-components';
import { hexPercentValues } from '@/styles';
-import type { NotificationType } from '@/types';
+import { NOTIFICATION_TYPE } from '@/types';
interface Props {
orientation?: 'horizontal' | 'vertical';
- type?: NotificationType; // this is to apply coloring to the divider
+ type?: NOTIFICATION_TYPE; // this is to apply coloring to the divider
thickness?: number;
length?: string;
margin?: string;
diff --git a/frontend/webapp/reuseable-components/icon-button/index.tsx b/frontend/webapp/reuseable-components/icon-button/index.tsx
index 07e803bc2..149d976c5 100644
--- a/frontend/webapp/reuseable-components/icon-button/index.tsx
+++ b/frontend/webapp/reuseable-components/icon-button/index.tsx
@@ -53,9 +53,9 @@ const Ping = styled.div<{ $color: Props['pingColor'] }>`
}
`;
-export const IconButton: React.FC = ({ children, onClick, withPing, pingColor }) => {
+export const IconButton: React.FC = ({ children, onClick, withPing, pingColor, ...props }) => {
return (
-