Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f24d072
chore: notification code refactoring
shivani170 Jan 22, 2026
829218e
feat: notification code refactoring
shivani170 Jan 22, 2026
dcfec36
feat: events integration in add notification component
shivani170 Jan 22, 2026
9c45fec
fix: approval event icons on notification table
shivani170 Jan 22, 2026
191733b
fix: css fixes in notification tab
shivani170 Jan 27, 2026
e621be6
feat: icon in modify recipient popup
shivani170 Jan 29, 2026
1ede8b3
feat: base row and checekbox handling
shivani170 Jan 30, 2026
802d965
chore: add notification fix for icon positioning of config approval
shivani170 Jan 30, 2026
6633bee
feat: tooltip added in modify recipient popup for disabled options
shivani170 Jan 30, 2026
69c9ff6
chore: optimized check event
shivani170 Jan 30, 2026
8ee8be4
chore: css fixes
shivani170 Jan 30, 2026
4f373b9
chore: position of tooltip fixed
shivani170 Jan 30, 2026
20a8ee7
chore: version bump
shivani170 Jan 30, 2026
074faec
chore: recipient popup toggle code optimization
shivani170 Jan 30, 2026
88f9a00
chore: css fixes
shivani170 Jan 30, 2026
e68c5e2
chore: add notification position fix
shivani170 Feb 5, 2026
0375d37
chore: renderEmailAgentSelector ui fix
shivani170 Feb 6, 2026
75feb38
chore: recipient name fix
shivani170 Feb 6, 2026
d6e155b
Merge branch 'develop' into feat/notification-events
shivani170 Feb 13, 2026
ad269a6
Merge branch 'develop' into feat/notification-ui-revamp
shivani170 Feb 13, 2026
d4415e3
feat: notification ui fixes
shivani170 Feb 16, 2026
ab838f1
Merge branch feat/notification-events into feat/notification-ui-revamp
shivani170 Feb 16, 2026
493b484
fix: css fixes
shivani170 Feb 17, 2026
2f14b27
chore: css fixes
shivani170 Feb 18, 2026
ae92d62
fix: added clear button to the bulk widget
shivani170 Feb 27, 2026
739f03a
chore: image change for deployment icon
shivani170 Feb 27, 2026
45179b6
Merge branch 'main' into feat/notification-events
shivani170 Feb 27, 2026
b51958d
yarn lock merge
shivani170 Feb 27, 2026
ac2a580
Merge branch 'feat/notification-events' into feat/notification-ui-revamp
shivani170 Feb 27, 2026
a3531c7
Merge pull request #3060 from devtron-labs/feat/notification-ui-revamp
shivani170 Feb 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"homepage": "/dashboard",
"dependencies": {
"@devtron-labs/devtron-fe-common-lib": "1.23.0",
"@devtron-labs/devtron-fe-common-lib": "1.23.1",
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
"@rjsf/core": "^5.13.3",
"@rjsf/utils": "^5.13.3",
Expand Down
485 changes: 250 additions & 235 deletions src/components/notifications/AddNotification.tsx

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions src/components/notifications/AddNotificationButton.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Button
text="Add Notification"
variant={ButtonVariantType.primary}
size={ComponentSizeType.medium}
onClick={createNewNotification}
dataTestId="add-notification-button"
startIcon={<Icon name="ic-add" color={null} />}
/>
)
}
109 changes: 109 additions & 0 deletions src/components/notifications/BulkMultiSelectWidget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* 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,
toggleAllNotification,
}: BulkMultiSelectTagWidgetType) => {
const renderModifyEventPopUpBody = () => (
<PopupMenu.Body>
<ModifyRecipientPopUp
events={events}
applyModifyEvents={applyModifyEvents}
onChangeCheckboxHandler={onChangeCheckboxHandler}
selectedNotificationList={selectedNotificationList}
/>
</PopupMenu.Body>
)
return (
<DraggableWrapper
dragSelector=".drag-selector"
positionVariant={DraggablePositionVariant.PARENT_BOTTOM_CENTER}
parentRef={parentRef}
zIndex="calc(var(--modal-index) - 1)"
>
<div className="flex dc__gap-8 pt-12 pb-12 pr-12 pl-8 bulk-selection-widget br-8">
<DraggableButton dragClassName="drag-selector" />
<div className="fs-13 lh-20 fw-6 flex dc__gap-12">
<span className="flex dc__gap-2 bcb-5 cn-0 br-4 pr-6 pl-6">{selectedIdentifiersCount}</span>
<span className="cn-9">Selected</span>
</div>
<div className="dc__divider h-16" />
<div className="flex left dc__gap-4">
<PopupMenu
onToggleCallback={(isOpen) => {
if (isOpen) {
onOpenEditNotificationMenu()
}
}}
>
<PopupMenu.Button rootClassName="popup-button--notification-tab">
<Icon
name="ic-bell"
color={null}
size={20}
tooltipProps={{ content: 'Modify events', alwaysShowTippyOnHover: true }}
/>
</PopupMenu.Button>
{renderModifyEventPopUpBody()}
</PopupMenu>

<Button
dataTestId="button__modify-recipients"
icon={<Icon name="ic-users" color={null} />}
variant={ButtonVariantType.borderLess}
style={ButtonStyleType.neutral}
ariaLabel="Modify Recipients"
onClick={showModifyModal}
showAriaLabelInTippy
/>

<Button
dataTestId="notification-delete-button"
icon={<Icon name="ic-delete" color={null} />}
variant={ButtonVariantType.borderLess}
style={ButtonStyleType.neutral}
ariaLabel="Delete Notifications"
onClick={showDeleteModal}
showAriaLabelInTippy
/>

<div className="dc__divider h-16" />

<Button
dataTestId="notification-delete-button"
icon={<Icon name="ic-close-large" color={null} />}
variant={ButtonVariantType.borderLess}
style={ButtonStyleType.neutral}
ariaLabel="Clear"
onClick={toggleAllNotification}
showAriaLabelInTippy
/>
</div>
</div>
</DraggableWrapper>
)
}
86 changes: 86 additions & 0 deletions src/components/notifications/ModifyRecipientPopUp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
Button,
ButtonVariantType,
Checkbox,
ComponentSizeType,
Icon,
IconName,
Tooltip,
} from '@devtron-labs/devtron-fe-common-lib'

import { EVENT_ICONS, EVENT_LABEL, EVENTS } from './constants'
import { ModifyRecipientPopUpType, NotificationPipelineType } from './types'

export const ModifyRecipientPopUp = ({
events,
applyModifyEvents,
onChangeCheckboxHandler,
selectedNotificationList,
}: ModifyRecipientPopUpType) => {
const getDisabledLabel = (value) => {
// If BASE type is present among all, only CONFIG_APPROVAL should be enabled
if (selectedNotificationList.some((row) => row.pipelineType === NotificationPipelineType.BASE)) {
return value !== EVENTS.CONFIG_APPROVAL
}

// If CI is available, disable CONFIG_APPROVAL and IMAGE_APPROVAL
if (selectedNotificationList.some((row) => row.pipelineType === NotificationPipelineType.CI)) {
return value === EVENTS.CONFIG_APPROVAL || value === EVENTS.IMAGE_APPROVAL
}

return false
}

const options = Object.values(EVENTS).map((value) => ({
label: EVENT_LABEL[value],
value,
icon: EVENT_ICONS[value],
isDisabled: getDisabledLabel(value),
}))

return (
<div>
<ul className="dc__kebab-menu__list kebab-menu__list--notification-tab ">
{options.map((option) => (
<Tooltip
content={option.isDisabled ? 'Cannot edit for the selected resource types.' : ''}
placement="top"
className="mxh-210 dc__overflow-auto dc__word-break"
alwaysShowTippyOnHover={option.isDisabled}
>
<li
key={option.value}
className={`dc__kebab-menu__list-item flex-justify flex ${option.isDisabled ? 'dc__disabled' : ''}`}
>
<div className="flex left dc__gap-8">
<Icon name={option.icon as IconName} color={null} />
<span>{option.label}</span>
</div>

<Checkbox
rootClassName="mb-0"
isChecked={events[option.value].isChecked}
value={events[option.value].value}
onChange={(e) => onChangeCheckboxHandler(e, option.value)()}
disabled={option.isDisabled}
>
<span />
</Checkbox>
</li>
</Tooltip>
))}
</ul>

<div className="p-8">
<Button
dataTestId="apply-recipient"
text="Apply"
variant={ButtonVariantType.primary}
size={ComponentSizeType.medium}
onClick={applyModifyEvents}
fullWidth
/>
</div>
</div>
)
}
89 changes: 31 additions & 58 deletions src/components/notifications/ModifyRecipientsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,18 @@
*/

import { Component } from 'react'
import { showError, Progressing, VisibleModal, RadioGroup, RadioGroupItem, ToastVariantType, ToastManager } from '@devtron-labs/devtron-fe-common-lib'
import {
showError,
VisibleModal,
RadioGroup,
RadioGroupItem,
ToastVariantType,
ToastManager,
preventDefault,
Button,
ButtonVariantType,
ButtonStyleType,
} from '@devtron-labs/devtron-fe-common-lib'
import CreatableSelect from 'react-select/creatable'
import { ReactComponent as Close } from '../../assets/icons/ic-close.svg'
import { ReactComponent as Slack } from '../../assets/icons/slack-logo.svg'
Expand All @@ -25,40 +36,7 @@ import { ReactComponent as Webhook } from '../../assets/icons/ic-CIWebhook.svg'
import { updateNotificationRecipients } from './notifications.service'
import { multiSelectStyles, DropdownIndicator, MultiValueLabel, Option } from './notifications.util'
import './notifications.scss'
import { EMAIL_AGENT } from './types'

interface ModifyRecipientsModalProps {
channelList: SelectedRecipientType[]
onSaveSuccess: () => void
closeModifyRecipientsModal: () => void
notificationListFromParent: {
id: number
providers: { dest: string; configId: number; recipient: string; name?: string }[]
}[]
}

interface RecipientType {
configId: number
recipient: string
dest: string
name?: string
}

interface SelectedRecipientType {
__isNew__?: boolean
label: string
value: string
data: RecipientType
}

interface ModifyRecipientsModalState {
isLoading: boolean
savedRecipients: RecipientType[]
selectedRecipient: SelectedRecipientType[]
showEmailAgents: boolean
selectedEmailAgent: string
recipientWithoutEmailAgent: boolean
}
import { EMAIL_AGENT, ModifyRecipientsModalProps, ModifyRecipientsModalState } from './types'

export class ModifyRecipientsModal extends Component<ModifyRecipientsModalProps, ModifyRecipientsModalState> {
constructor(props) {
Expand All @@ -71,8 +49,6 @@ export class ModifyRecipientsModal extends Component<ModifyRecipientsModalProps,
selectedEmailAgent: null,
recipientWithoutEmailAgent: false,
}
this.saveRecipients = this.saveRecipients.bind(this)
this.changeEmailAgent = this.changeEmailAgent.bind(this)
}

componentDidMount() {
Expand Down Expand Up @@ -130,8 +106,8 @@ export class ModifyRecipientsModal extends Component<ModifyRecipientsModalProps,
this.setState(state)
}

saveRecipients(e) {
e.preventDefault()
saveRecipients = (e) => {
preventDefault(e)
if (
this.state.selectedRecipient.length > 0 &&
this.state.recipientWithoutEmailAgent &&
Expand Down Expand Up @@ -164,13 +140,13 @@ export class ModifyRecipientsModal extends Component<ModifyRecipientsModalProps,
})
}

changeEmailAgent(event: any): void {
changeEmailAgent = (event: any): void => {
const state = { ...this.state }
state.selectedEmailAgent = event.target.value
this.setState(state)
}

renderEmailAgentSelector() {
renderEmailAgentSelector = () => {
if (this.state.selectedRecipient.length > 0 && this.state.recipientWithoutEmailAgent) {
return (
<div className="form__row">
Expand Down Expand Up @@ -285,26 +261,23 @@ export class ModifyRecipientsModal extends Component<ModifyRecipientsModalProps,
<div style={{ marginBottom: '60px' }} />
)}
</div>
<div>{this.renderEmailAgentSelector()}</div>
{this.renderEmailAgentSelector()}
</div>
<div className="form__button-group-bottom flex right">
<button
type="button"
className="cta cancel mr-16"
tabIndex={3}
<div className="form__button-group-bottom flex right dc__gap-16">
<Button
dataTestId="button__cancel"
text="Cancel"
onClick={this.props.closeModifyRecipientsModal}
>
Cancel
</button>
<button
type="submit"
className="cta"
tabIndex={4}
disabled={this.state.isLoading}
variant={ButtonVariantType.secondary}
style={ButtonStyleType.neutral}
/>
<Button
dataTestId="button__save-changes"
text="Save Changes"
onClick={this.saveRecipients}
>
{this.state.isLoading ? <Progressing /> : 'Save Changes'}
</button>
variant={ButtonVariantType.primary}
disabled={this.state.isLoading}
/>
</div>
</>
)
Expand Down
Loading
Loading