From e68c5e216a1362af9f02e8bea07b49432752af52 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 5 Feb 2026 15:18:49 +0530 Subject: [PATCH 1/4] chore: add notification position fix --- .../notifications/NotificationTab.tsx | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/components/notifications/NotificationTab.tsx b/src/components/notifications/NotificationTab.tsx index 4cb21582c3..42802662c9 100644 --- a/src/components/notifications/NotificationTab.tsx +++ b/src/components/notifications/NotificationTab.tsx @@ -571,8 +571,12 @@ export class NotificationTab extends Component { row.appliedFilters.project?.length || row.appliedFilters.cluster?.length ? ( <> - {row.pipelineType === NotificationPipelineType.BASE ? 'Base configuration matching:' : 'All existing and future deployment pipelines matching:'} -
+ + {row.pipelineType === NotificationPipelineType.BASE + ? 'Base configuration matching:' + : 'All existing and future deployment pipelines matching:'} + +
{row.appliedFilters.project.map((element) => ( { renderBody = () => (
-
+
+ {this.renderBulkOptions()} + {this.renderPipelineList()} + {this.renderPagination()} +
) From d4415e322fdd9e9431585a86b3ef970b3dcc3da0 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Mon, 16 Feb 2026 18:21:28 +0530 Subject: [PATCH 2/4] feat: notification ui fixes --- .../notifications/AddNotification.tsx | 17 +- .../notifications/BulkMultiSelectWidget.tsx | 96 +++++++++++ .../notifications/NotificationTab.tsx | 152 +++++++----------- .../notifications/Notifications.tsx | 79 +++++++-- .../notifications/notifications.scss | 20 ++- .../notifications/notifications.util.tsx | 4 +- src/components/notifications/types.tsx | 21 ++- 7 files changed, 266 insertions(+), 123 deletions(-) create mode 100644 src/components/notifications/BulkMultiSelectWidget.tsx diff --git a/src/components/notifications/AddNotification.tsx b/src/components/notifications/AddNotification.tsx index 8f6602279a..363fcd92a5 100644 --- a/src/components/notifications/AddNotification.tsx +++ b/src/components/notifications/AddNotification.tsx @@ -278,11 +278,17 @@ export class AddNotification extends Component Resource Application Name - Type Env/Branch Events @@ -731,6 +736,7 @@ export class AddNotification extends Component + {renderPipelineTypeIcon(row, 20)} {row.appliedFilters.length ? ( <> @@ -753,7 +759,6 @@ export class AddNotification extends Component {row?.appName} - {renderPipelineTypeIcon(row)} {_isCi && ( diff --git a/src/components/notifications/BulkMultiSelectWidget.tsx b/src/components/notifications/BulkMultiSelectWidget.tsx new file mode 100644 index 0000000000..3d4b524ff8 --- /dev/null +++ b/src/components/notifications/BulkMultiSelectWidget.tsx @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024. Devtron Inc. + */ + +import { + Button, + ButtonStyleType, + ButtonVariantType, + DraggableButton, + DraggablePositionVariant, + DraggableWrapper, + Icon, + PopupMenu, +} from '@devtron-labs/devtron-fe-common-lib' + +import { ModifyRecipientPopUp } from './ModifyRecipientPopUp' +import { BulkMultiSelectTagWidgetType } from './types' + +export const BulkMultiSelectTagWidget = ({ + parentRef, + selectedIdentifiersCount, + showDeleteModal, + events, + applyModifyEvents, + onChangeCheckboxHandler, + selectedNotificationList, + onOpenEditNotificationMenu, + showModifyModal, +}: BulkMultiSelectTagWidgetType) => { + const renderModifyEventPopUpBody = () => ( + + + + ) + return ( + +
+ +
+ {selectedIdentifiersCount} + Selected +
+
+
+
+
+
+ + ) +} diff --git a/src/components/notifications/NotificationTab.tsx b/src/components/notifications/NotificationTab.tsx index 42802662c9..ad042090e3 100644 --- a/src/components/notifications/NotificationTab.tsx +++ b/src/components/notifications/NotificationTab.tsx @@ -58,8 +58,12 @@ import { getRecipientChipStartIcon, renderPipelineTypeIcon } from './notificatio import { NotificationPipelineType, NotificationTabState } from './types' import { ModifyRecipientPopUp } from './ModifyRecipientPopUp' import { EVENT_ID } from './constants' +import { BulkMultiSelectTagWidget } from './BulkMultiSelectWidget' +import React from 'react' export class NotificationTab extends Component { + bulkMultiSelectWidgetRef = React.createRef() + constructor(props) { super(props) this.state = { @@ -105,7 +109,6 @@ export class NotificationTab extends Component { deleting: false, confirmation: false, singleDeletedId: 0, - disableEdit: false, selectedNotificationList: [], } } @@ -164,8 +167,8 @@ export class NotificationTab extends Component { })) this.setState({ channelList: list }) }) - .catch((error) => { - this.setState({ disableEdit: true }) + .catch(() => { + this.props.toggleDisableEdit(true) }) } @@ -376,7 +379,7 @@ export class NotificationTab extends Component { } createNewNotification = () => { - if (this.state.disableEdit) { + if (this.props.disableEdit) { ToastManager.showToast({ variant: ToastVariantType.notAuthorized, description: TOAST_ACCESS_DENIED.SUBTITLE, @@ -408,7 +411,7 @@ export class NotificationTab extends Component { ) validateAccess = (updateState): void => { - if (this.state.disableEdit) { + if (this.props.disableEdit) { ToastManager.showToast({ variant: ToastVariantType.notAuthorized, description: TOAST_ACCESS_DENIED.SUBTITLE, @@ -423,7 +426,7 @@ export class NotificationTab extends Component { } applyModifyEvents = () => { - if (this.state.disableEdit) { + if (this.props.disableEdit) { ToastManager.showToast({ variant: ToastVariantType.notAuthorized, description: TOAST_ACCESS_DENIED.SUBTITLE, @@ -437,62 +440,6 @@ export class NotificationTab extends Component { this.validateAccess({ showModifyRecipientsModal: !this.state.showModifyRecipientsModal }) } - renderModifyEventPopUpBody = () => ( - - - - ) - - renderBulkOptions = () => { - if (this.state.headerCheckbox.isChecked) { - return ( -
-
- ) - } - } - onChangePipelineCheckbox = (e) => { stopPropagation(e) this.toggleAllNotification() @@ -512,12 +459,12 @@ export class NotificationTab extends Component { } renderPipelineList = () => ( - - +
+ - - + - + + {this.state.notificationList.map((row) => { const _isCi = row.branch && row.pipelineType === NotificationPipelineType.CI let _isWebhookCi @@ -545,6 +496,11 @@ export class NotificationTab extends Component { _isWebhookCi = false } } + const isBaseConfig = + row.appliedFilters.environment?.length || + row.appliedFilters.application.length || + row.appliedFilters.project?.length || + row.appliedFilters.cluster?.length return ( { - -
+ { Resource + + Resource + Application NameType Env/Branch Events Recipients
- {row.pipelineName ? row.pipelineName : ''} - {row.appliedFilters.environment?.length || - row.appliedFilters.application.length || - row.appliedFilters.project?.length || - row.appliedFilters.cluster?.length ? ( - <> - - {row.pipelineType === NotificationPipelineType.BASE - ? 'Base configuration matching:' - : 'All existing and future deployment pipelines matching:'} - + +
+ {!isBaseConfig && renderPipelineTypeIcon(row)} + {row.pipelineName ? row.pipelineName : ''} +
+ + {isBaseConfig ? ( +
+
+ {renderPipelineTypeIcon(row)} + + + {row.pipelineType === NotificationPipelineType.BASE + ? 'Base configuration matching:' + : `All existing and future ${row.pipelineType === NotificationPipelineType.CI ? 'build' : 'deployment'} pipelines matching:`} + +
{row.appliedFilters.project.map((element) => ( { /> ))}
- +
) : null}
{row?.appName}{renderPipelineTypeIcon(row)} {_isCi && ( @@ -691,7 +651,7 @@ export class NotificationTab extends Component { -
+
{row.providers.map((p) => ( { renderBody = () => (
-
-
- {this.renderBulkOptions()} {this.renderPipelineList()} {this.renderPagination()}
@@ -800,11 +749,24 @@ export class NotificationTab extends Component { ) } return ( -
+
{this.renderHostErrorMessage()} {this.renderBody()} {this.state.showDeleteDialog && this.renderDeleteDialog()} {this.renderModifyRecipients()} + {this.state.headerCheckbox.isChecked && ( + row.isSelected).length} + showDeleteModal={this.showDeleteModal} + events={this.state.events} + applyModifyEvents={this.applyModifyEvents} + onChangeCheckboxHandler={this.onChangeCheckboxHandler} + selectedNotificationList={this.state.selectedNotificationList} + onOpenEditNotificationMenu={this.onOpenEditNotificationMenu} + showModifyModal={this.showModifyModal} + /> + )}
) } diff --git a/src/components/notifications/Notifications.tsx b/src/components/notifications/Notifications.tsx index d97c63938f..ac020bc373 100644 --- a/src/components/notifications/Notifications.tsx +++ b/src/components/notifications/Notifications.tsx @@ -16,29 +16,72 @@ import { Component } from 'react' import { Switch, Route, Redirect, RouteComponentProps } from 'react-router-dom' -import { DOCUMENTATION, ErrorScreenNotAuthorized, FeatureTitleWithInfo, TabGroup } from '@devtron-labs/devtron-fe-common-lib' +import { + Button, + ButtonVariantType, + ComponentSizeType, + ErrorScreenNotAuthorized, + FeatureTitleWithInfo, + Icon, + TabGroup, + TOAST_ACCESS_DENIED, + ToastManager, + ToastVariantType, +} from '@devtron-labs/devtron-fe-common-lib' import { ConfigurationTab } from './ConfigurationTab' import { NotificationTab } from './NotificationTab' import { ErrorBoundary } from '../common' -import { HEADER_TEXT } from '../../config' +import { HEADER_TEXT, URLS } from '../../config' import './notifications.scss' +import { NotificationsProps, NotificationsState } from './types' -interface NotificationsProps extends RouteComponentProps<{}> { - isSuperAdmin: boolean -} +export default class Notifications extends Component { + + constructor(props) { + super(props) + this.state = { + disableEdit: false, + } + } + + createNewNotification = () => { + if (this.state.disableEdit) { + ToastManager.showToast({ + variant: ToastVariantType.notAuthorized, + description: TOAST_ACCESS_DENIED.SUBTITLE, + }) + } else { + this.props.history.push(URLS.APPLICATION_MANAGEMENT_CONFIGURATIONS_NOTIFICATIONS_ADD_NEW) + } + } + + toggleDisableEdit = (disableEdit: boolean) => { + this.setState({ disableEdit }) + } -export default class Notifications extends Component { renderNotificationHeader() { return (
- HEADER_TEXT.NOTIFICATIONS.description} - docLink="GLOBAL_CONFIG_NOTIFICATION" - showInfoIconTippy - dataTestId="notifications-feature-title" - /> +
+ HEADER_TEXT.NOTIFICATIONS.description} + docLink="GLOBAL_CONFIG_NOTIFICATION" + showInfoIconTippy + dataTestId="notifications-feature-title" + /> +
+
+
{
- + ( + + )} + /> diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 2461d7a3f1..d788cf983d 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -54,14 +54,24 @@ color: var(--N500); text-transform: capitalize; display: table-row; + + th { + padding: 10px; + vertical-align: left; + } +} + +thead { + position: sticky; + top: 0; + z-index: 2; box-shadow: inset 0 -1px 0 0 var(--N200); - padding: 10px 0 16px 0; + background-color: var(--bg-primary); } .pipeline-list__checkbox { width: 24px; padding: 10px; - padding-top: 15px; } .pipeline-list__pipeline-name { @@ -125,8 +135,8 @@ } -.configuration-drawer{ - .webhook-config-modal{ +.configuration-drawer { + .webhook-config-modal { display: grid; grid-template-columns: 1fr 280px; } @@ -331,4 +341,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/components/notifications/notifications.util.tsx b/src/components/notifications/notifications.util.tsx index 2e87a3e52d..06e8ff4c0a 100644 --- a/src/components/notifications/notifications.util.tsx +++ b/src/components/notifications/notifications.util.tsx @@ -155,7 +155,7 @@ export const Option = (props) => { ) } -export const renderPipelineTypeIcon = (row) => { +export const renderPipelineTypeIcon = (row, size = 24 as IconBaseSizeType ) => { if (row.isVirtualEnvironment) { return } @@ -163,7 +163,7 @@ export const renderPipelineTypeIcon = (row) => { return } if (row.pipelineType === NotificationPipelineType.BASE || row.type === NotificationPipelineType.BASE ) { - return + return } return } diff --git a/src/components/notifications/types.tsx b/src/components/notifications/types.tsx index 0b12c8045b..48795e04af 100644 --- a/src/components/notifications/types.tsx +++ b/src/components/notifications/types.tsx @@ -313,7 +313,6 @@ export interface NotificationTabState { deleting: boolean confirmation: boolean singleDeletedId: number - disableEdit: boolean selectedNotificationList: NotificationConfiguration[] } @@ -450,3 +449,23 @@ export interface ModifyRecipientPopUpType extends Pick void onChangeCheckboxHandler: (e, value) => () => void } + +export interface NotificationsProps extends RouteComponentProps<{}> { + isSuperAdmin: boolean +} + +export interface NotificationsState { + disableEdit: boolean +} + +export interface BulkMultiSelectTagWidgetType { + parentRef: React.RefObject + selectedIdentifiersCount: number + showDeleteModal: () => void + events: NotificationTabEvents + applyModifyEvents: () => void + onChangeCheckboxHandler: (e, value) => () => void + selectedNotificationList: NotificationConfiguration[] + onOpenEditNotificationMenu: () => void + showModifyModal: () => void +} From 493b48403c23afa55e7f30968a448f226b9c680b Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 17 Feb 2026 13:06:07 +0530 Subject: [PATCH 3/4] fix: css fixes --- .../notifications/AddNotification.tsx | 34 ++++++++++--------- .../notifications/BulkMultiSelectWidget.tsx | 22 ++++++------ .../notifications/Notifications.tsx | 2 +- .../notifications/notifications.util.tsx | 4 +-- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/components/notifications/AddNotification.tsx b/src/components/notifications/AddNotification.tsx index 363fcd92a5..39cb0d7537 100644 --- a/src/components/notifications/AddNotification.tsx +++ b/src/components/notifications/AddNotification.tsx @@ -42,6 +42,7 @@ import { IconsProps, ButtonComponentType, stopPropagation, + BreadCrumb, } from '@devtron-labs/devtron-fe-common-lib' import { components } from 'react-select' import CreatableSelect from 'react-select/creatable' @@ -736,7 +737,7 @@ export class AddNotification extends Component
- {renderPipelineTypeIcon(row, 20)} + {renderPipelineTypeIcon(row, 16)} {row.appliedFilters.length ? ( <> @@ -1063,28 +1064,29 @@ export class AddNotification extends Component { + const breadcrumbs = [ + { + name: 'Notifications', + to: URLS.APPLICATION_MANAGEMENT_CONFIGURATIONS_NOTIFICATIONS, + }, + { + name: 'Add Notification', + to: '', + }, + ] + return + } + render() { return (
-
{this.renderAddCard()} {this.renderShowSlackConfigModal()} diff --git a/src/components/notifications/BulkMultiSelectWidget.tsx b/src/components/notifications/BulkMultiSelectWidget.tsx index 3d4b524ff8..d388c07298 100644 --- a/src/components/notifications/BulkMultiSelectWidget.tsx +++ b/src/components/notifications/BulkMultiSelectWidget.tsx @@ -51,17 +51,7 @@ export const BulkMultiSelectTagWidget = ({ Selected
-
-
diff --git a/src/components/notifications/Notifications.tsx b/src/components/notifications/Notifications.tsx index ac020bc373..dcc0473ac5 100644 --- a/src/components/notifications/Notifications.tsx +++ b/src/components/notifications/Notifications.tsx @@ -74,7 +74,7 @@ export default class Notifications extends Component