import classNames from 'classnames'; import { capitalize, get, isEmpty } from 'lodash'; import React from 'react'; import { from as observableFrom, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { AppNotificationsService } from './AppNotificationsService'; import type { Application } from '../application'; import type { INotification, INotificationTypeConfig } from '../domain'; import { EditNotificationModal } from './modal/EditNotificationModal'; import { NotificationTransformer } from './notification.transformer'; import { Registry } from '../registry'; export interface INotificationsListProps { application?: Application; level: string; stageType?: string; sendNotifications?: boolean; handleSendNotificationsChanged?: (event: React.ChangeEvent) => void; notifications: INotification[]; updateNotifications: (notifications: INotification[]) => void; } export interface INotificationsListState { indexEdited: number; isNotificationsDirty: boolean; supportedNotificationTypes: string[]; } export class NotificationsList extends React.Component { private destroy$ = new Subject(); constructor(props: INotificationsListProps) { super(props); this.state = { indexEdited: null, isNotificationsDirty: false, supportedNotificationTypes: Registry.pipeline .getNotificationTypes() .map((type: INotificationTypeConfig) => type.key), }; } public componentDidMount() { if (this.props.level === 'application') { this.revertNotificationChanges(); } } public componentWillUnmount() { this.destroy$.next(); } private getEditFunctionality = (notification?: INotification, index?: number) => { if (this.props.level === 'application') { this.showEditModal(notification, index); } else { this.editNotification(notification, index); } }; private addNotification = () => { this.getEditFunctionality(); }; private editNotification = (notification?: INotification, index?: number) => { const { level, notifications, stageType, updateNotifications } = this.props; EditNotificationModal.show({ notification: notification || { level, when: [] }, level: level, stageType, }) .then((newNotification) => { const notificationsCopy = notifications || []; if (!notification) { updateNotifications(notificationsCopy.concat(newNotification)); } else { const update = [...notificationsCopy]; update[index] = newNotification; updateNotifications(update); } this.setState({ isNotificationsDirty: true }); }) .catch(() => {}); }; private saveAppNotifications = (newNotifications: INotification[]) => { const { application } = this.props; const toSaveNotifications: any = { application: application.name, }; newNotifications.forEach((n) => { if (isEmpty(get(toSaveNotifications, n.type))) { toSaveNotifications[n.type] = []; } toSaveNotifications[n.type].push(n); }); return AppNotificationsService.saveNotificationsForApplication(application.name, toSaveNotifications).then(() => this.revertNotificationChanges(), ); }; public editAppNotification = (newNotification: INotification) => { const { notifications } = this.props; const { indexEdited } = this.state; let notificationsToSave = notifications || []; if (indexEdited || indexEdited === 0) { notificationsToSave[indexEdited] = newNotification; } else { notificationsToSave = notificationsToSave.concat(newNotification); } return this.saveAppNotifications(notificationsToSave); }; private showEditModal = (notification?: INotification, index?: number) => { const { level, stageType } = this.props; this.setState({ indexEdited: index }); EditNotificationModal.show({ notification: notification || { level, when: [] }, level, stageType, editNotification: this.editAppNotification, }); }; private removeNotification = (index: number) => { const notifications = [...this.props.notifications]; notifications.splice(index, 1); this.props.updateNotifications(notifications); this.setState({ isNotificationsDirty: true, }); }; private revertNotificationChanges = () => { /* we currently store application level notifications in front50 as a map indexed by type { "application": "ayuda", "email": [ { ... } ] } the code below unwraps it into a table friendly format and the saveAppNotifications code will write it back into the right format. We will change the format in front50 when we rewrite notifications to use CQL so this transformation is no longer needed */ const { application, updateNotifications } = this.props; const { supportedNotificationTypes } = this.state; observableFrom(AppNotificationsService.getNotificationsForApplication(application.name)) .pipe(takeUntil(this.destroy$)) .subscribe((notifications) => { const newNotifications = supportedNotificationTypes.reduce((acc, type) => { const typeNotifications = notifications[type]; if (typeNotifications && typeof typeNotifications !== 'string') { acc.push(...typeNotifications); } return acc; }, []); const applicationNotifications = newNotifications.filter( (allow) => allow !== undefined && allow.level === 'application', ); updateNotifications(applicationNotifications); }); this.setState({ isNotificationsDirty: false }); }; public render() { const { level, handleSendNotificationsChanged, notifications, sendNotifications, stageType } = this.props; const { isNotificationsDirty } = this.state; return ( <> {level === 'stage' && stageType !== 'manualJudgment' && (
)} {(level !== 'stage' || sendNotifications) && (
{notifications && notifications.length > 0 && ( )} {notifications && notifications.map((n, i) => { return ( ); })}
Type Details Notify When Actions
{capitalize(n.type)} {NotificationTransformer.getNotificationDetails(n)} {n.when && n.when.map((w, k) => (
{NotificationTransformer.getNotificationWhenDisplayName(w, level, stageType)}
))}
{!n.inherited && ( <> )} {n.inherited && Inherited from template}
)} {level === 'application' && (
{isNotificationsDirty && ( )}
{isNotificationsDirty && ( )} {isNotificationsDirty === false && ( In sync with server )}
)} ); } }