diff --git a/src/components/notifications/AddNotification.tsx b/src/components/notifications/AddNotification.tsx
index 8f6602279a..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'
@@ -278,11 +279,17 @@ export class AddNotification extends Component
+ {renderPipelineTypeIcon(row, 16)}
{row.appliedFilters.length ? (
<>
@@ -753,7 +760,6 @@ export class AddNotification extends Component
| {row?.appName} |
- {renderPipelineTypeIcon(row)} |
{_isCi && (
@@ -1058,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 (
- }
- component={ButtonComponentType.link}
- linkProps={{
- to: URLS.APPLICATION_MANAGEMENT_CONFIGURATIONS_NOTIFICATIONS,
- }}
- ariaLabel="Back to notifications"
- showAriaLabelInTippy={false}
- variant={ButtonVariantType.borderLess}
- style={ButtonStyleType.neutral}
- size={ComponentSizeType.small}
- />
- Add Notifications
+ {this.renderBreadcrumbs()}
{this.renderAddCard()}
{this.renderShowSlackConfigModal()}
diff --git a/src/components/notifications/AddNotificationButton.tsx b/src/components/notifications/AddNotificationButton.tsx
new file mode 100644
index 0000000000..c8bbb07fab
--- /dev/null
+++ b/src/components/notifications/AddNotificationButton.tsx
@@ -0,0 +1,38 @@
+import { useHistory } from 'react-router-dom'
+
+import {
+ Button,
+ ButtonVariantType,
+ ComponentSizeType,
+ Icon,
+ TOAST_ACCESS_DENIED,
+ ToastManager,
+ ToastVariantType,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { URLS } from '@Config/routes'
+
+export const AddNotificationButton = ({ disableEdit }: { disableEdit: boolean }) => {
+ const history = useHistory()
+ const createNewNotification = () => {
+ if (disableEdit) {
+ ToastManager.showToast({
+ variant: ToastVariantType.notAuthorized,
+ description: TOAST_ACCESS_DENIED.SUBTITLE,
+ })
+ } else {
+ history.push(URLS.APPLICATION_MANAGEMENT_CONFIGURATIONS_NOTIFICATIONS_ADD_NEW)
+ }
+ }
+
+ return (
+ }
+ />
+ )
+}
diff --git a/src/components/notifications/BulkMultiSelectWidget.tsx b/src/components/notifications/BulkMultiSelectWidget.tsx
new file mode 100644
index 0000000000..d388c07298
--- /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
+
+
+
+ {
+ if (isOpen) {
+ onOpenEditNotificationMenu()
+ }
+ }}
+ >
+
+
+
+ {renderModifyEventPopUpBody()}
+
+
+ }
+ variant={ButtonVariantType.borderLess}
+ style={ButtonStyleType.neutral}
+ ariaLabel="Modify Recipients"
+ onClick={showModifyModal}
+ showAriaLabelInTippy
+ />
+
+ }
+ variant={ButtonVariantType.borderLess}
+ style={ButtonStyleType.neutral}
+ ariaLabel="Delete Notifications"
+ onClick={showDeleteModal}
+ showAriaLabelInTippy
+ />
+
+
+
+ )
+}
diff --git a/src/components/notifications/NotificationTab.tsx b/src/components/notifications/NotificationTab.tsx
index 0636f27c72..34f6fb19cd 100644
--- a/src/components/notifications/NotificationTab.tsx
+++ b/src/components/notifications/NotificationTab.tsx
@@ -45,7 +45,7 @@ import {
import { InValidHostUrlWarningBlock } from '@Components/common'
import EmptyImage from '../../assets/img/ic-empty-notifications.png'
-import { URLS, ViewType } from '../../config'
+import { ViewType } from '../../config'
import { getHostURLConfiguration } from '../../services/service'
import { ModifyRecipientsModal } from './ModifyRecipientsModal'
import {
@@ -56,10 +56,14 @@ import {
} from './notifications.service'
import { getRecipientChipStartIcon, renderPipelineTypeIcon } from './notifications.util'
import { NotificationPipelineType, NotificationTabState } from './types'
-import { ModifyRecipientPopUp } from './ModifyRecipientPopUp'
import { EVENT_ID } from './constants'
+import { BulkMultiSelectTagWidget } from './BulkMultiSelectWidget'
+import React from 'react'
+import { AddNotificationButton } from './AddNotificationButton'
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)
})
}
@@ -375,28 +378,9 @@ export class NotificationTab extends Component {
)
}
- 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)
- }
- }
-
renderAddNotificationButton = () => (
- }
- />
+
)
-
renderGenericEmptyState = () => (
{
)
validateAccess = (updateState): void => {
- if (this.state.disableEdit) {
+ if (this.props.disableEdit) {
ToastManager.showToast({
variant: ToastVariantType.notAuthorized,
description: TOAST_ACCESS_DENIED.SUBTITLE,
@@ -423,7 +407,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 +421,6 @@ export class NotificationTab extends Component {
this.validateAccess({ showModifyRecipientsModal: !this.state.showModifyRecipientsModal })
}
- renderModifyEventPopUpBody = () => (
-
-
-
- )
-
- renderBulkOptions = () => {
- if (this.state.headerCheckbox.isChecked) {
- return (
-
- }
- variant={ButtonVariantType.borderLess}
- style={ButtonStyleType.neutral}
- ariaLabel="Delete Notifications"
- onClick={this.showDeleteModal}
- showAriaLabelInTippy
- />
- {
- if (isOpen) {
- this.onOpenEditNotificationMenu()
- }
- }}
- >
-
-
-
- {this.renderModifyEventPopUpBody()}
-
-
- }
- variant={ButtonVariantType.borderLess}
- style={ButtonStyleType.neutral}
- ariaLabel="Modify Recipients"
- onClick={this.showModifyModal}
- showAriaLabelInTippy
- />
-
- )
- }
- }
-
onChangePipelineCheckbox = (e) => {
stopPropagation(e)
this.toggleAllNotification()
@@ -513,11 +441,11 @@ export class NotificationTab extends Component {
renderPipelineList = () => (
-
+
- |
+ |
{
|
- Resource |
+
+
+ Resource
+ |
Application Name |
- Type |
Env/Branch |
Events |
Recipients |
|
+
+
{this.state.notificationList.map((row) => {
const _isCi = row.branch && row.pipelineType === NotificationPipelineType.CI
let _isWebhookCi
@@ -545,6 +477,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 (
{
-
- {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 && (
@@ -687,7 +632,7 @@ export class NotificationTab extends Component {
|
-
+
{row.providers.map((p) => (
{
renderBody = () => (
- }
- />
- {this.renderBulkOptions()}
- {this.renderPipelineList()}
- {this.renderPagination()}
+
+ {this.renderPipelineList()}
+ {this.renderPagination()}
+
)
@@ -792,11 +730,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..bd7fc92f7f 100644
--- a/src/components/notifications/Notifications.tsx
+++ b/src/components/notifications/Notifications.tsx
@@ -16,29 +16,55 @@
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'
+import { AddNotificationButton } from './AddNotificationButton'
-interface NotificationsProps extends RouteComponentProps<{}> {
- isSuperAdmin: boolean
-}
+export default class Notifications extends Component {
+
+ constructor(props) {
+ super(props)
+ this.state = {
+ disableEdit: false,
+ }
+ }
+
+ 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..45947f2983 100644
--- a/src/components/notifications/notifications.util.tsx
+++ b/src/components/notifications/notifications.util.tsx
@@ -155,17 +155,17 @@ export const Option = (props) => {
)
}
-export const renderPipelineTypeIcon = (row) => {
+export const renderPipelineTypeIcon = (row, size = 24 as IconBaseSizeType ) => {
if (row.isVirtualEnvironment) {
return
}
if (row.pipelineType === 'CI' || row.type === 'CI') {
- return
+ return
}
if (row.pipelineType === NotificationPipelineType.BASE || row.type === NotificationPipelineType.BASE ) {
- return
+ return
}
- return
+ return
}
export const getConfigTabIcons = (tab: ConfigurationsTabTypes, size: IconBaseSizeType = 24) => {
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
+}
| | | |