Visualizing the Notifications

Visualizing the Notifications

In my previous article, I showed how to create a notification solution that provides the ability to send notifications to the user (as In-App Notification, Microsoft Outlook or Microsoft Teams). In this post, I will demonstrate how to show the notifications that have been sent to the user on the Contact record as a virtual dataset pcf control.

Before showing the process, let’s take a look at the end result of the pcf control. When looking at the contact form, you will notice that I have added a pcf control that is displaying the list of notifications that have been sent is a notes like view, and provides several icons to perform actions on each of these notifications (which would be using the WebAPI). The image below shows you the results in a regular subgrid and the custom control. As this control is read only, some code is not required to handle read only or disabled states.

This blog post will not cover the entire pcf control, but just the base components. A link to download the source code will be posted on the bottom of this page.

The NotificationList control contains 4 base files. The Control Manifest, the Index.ts file, a NotificationList.tsx file and a stylesheet that helps determine the look and feel of the control. Let’s start going over some of the code and explain what is being done here.

The Control Manifest is pretty straight forward, and contains the dataset element and a property for the publisher prefix, as this is using individual fields to design the layout of the control. Of course this can be modified as needed. The code below only contains portions of the xml file. The full file is available for download.

<?xml version="1.0" encoding="utf-8" ?>
<manifest>
  <control namespace="MDT.VirtualControl.Core" constructor="NotificationList" version="0.0.1" display-name-key="NotificationList" description-key="NotificationList description" control-type="virtual" >
    <data-set name="notificationDataSet" display-name-key="Dataset_Notification_History" cds-data-set-options="displayCommandBar:true;displayViewSelector:true">
</data-set>
    <property name="publisherPrefix" display-name-key="Publisher_Prefix_Display_Key" description-key="Publisher_Prefix_Desc_Key" of-type="SingleLine.Text" usage="input" required="true" />
  </control>
</manifest>

The index file contains code for the updateView function passing the properties of the control as well as functions to open the notification, resent it or disable/hide the notification from the view. Let’s take at these functions:

    public updateView(context: ComponentFramework.Context<IInputs>): React.ReactElement {
        const props: INotificationListProps = { dataset: context.parameters.notificationDataSet, 
                                                publisherPrefix: context.parameters.publisherPrefix.raw!,
                                                openRecord: this.openRecord,
                                                hideRecord: this.deactivateRecord,
                                                resendNotifications: this.resendRecordNotifications,
                                                executeAction: this.executeAction
                                            };
        const iconProps: IActionButtonProps = { disabled: false, checked: false };
        return React.createElement(
            NotificationListControl, props, iconProps
        );
    }

The index file also contains the individual functions to open the notification, deactivate the notification record or resend the notification. Note that this is for demonstration purposes only, and the code has to be tested and modified for your own scenarios.

	private openRecord(id:string){
		let entityName:string = this._context.parameters.notificationDataSet.getTargetEntityType();

		let entityFormOptions = {
			entityName:  entityName,
			entityId: id
		};

		this._context.navigation.openForm(entityFormOptions);
	}    


    
    private deactivateRecord(id:string)
    {
        let entityName:string = this._context.parameters.notificationDataSet.getTargetEntityType();
        let data = 
        {
            "statecode": 1,
            "statuscode": 2        
        }

        this._context.webAPI.updateRecord(entityName, id, data).then(
            function success(result)
 { },
            function (error) { }
        )
    }

    private resendRecordNotifications(id:string)
    {
        let entityName:string = this._context.parameters.notificationDataSet.getTargetEntityType();
        let fieldName = this._context.parameters.publisherPrefix.raw! + "fieldName";
        let data = 
        {
            // ADD CODE TO UPDATE A FIELD THAT WILL TRIGGER A FLOW TO RESENT NOTIFICATIONS
        }

        this._context.webAPI.updateRecord(entityName, id, data).then(
            function success(result) {
 },
            function (error) { console.log(error.message); }
        )
    }

Finally, the Notification List tsx control contains the styling of the control which uses Stack and StackItems as well as IconProps and TooltipHosts to show the explanation of each of the Icons. There is some additional code in the file that was used for testing purposes of other view functionality which can be removed.

The actual class that extends the React Component is shown below, and can be customized to fit the needs.

export class NotificationListControl extends React.Component<INotificationListProps> {

  public render(): React.ReactNode {

    let ds = this.props.dataset; 
    const notifications: INotificationItem[]  = [];

    ds.sortedRecordIds.forEach((item) => {
      notifications.push( { notificationId: ds.records[item].getRecordId(), 
                            notificationTitle: ds.records[item].getFormattedValue(this.props.publisherPrefix + "title"), 
                            notificationBody: ds.records[item].getFormattedValue(this.props.publisherPrefix + "body"), 
                            notificationCategory: ds.records[item].getFormattedValue(this.props.publisherPrefix + "regardingid"), 
                            notificationDate: ds.records[item].getFormattedValue("createdon")}
                            );
    });

    return (
      <Stack tokens={outerStackTokens} className="fullWidth">
        {notifications.map(item => (
        <Stack verticalAlign='stretch' key={item.notificationId} styles={stackStyles} tokens={innerStackTokens}>
        <Stack.Item  align='stretch' key={item.notificationId} styles={stackItemStyles}>
              <div className="notificationHeader">
                <div className="notificationTitle">{item.notificationTitle}</div>  
                <div className="notificationIcons">
                  <TooltipHost content="Open Notification" id='openNotification' calloutProps={calloutProps} styles={hostStyles} >
                    <IconButton iconProps={OpenIcon} aria-label="Open Notification" onClick={() => this.props.openRecord(item.notificationId)} />
                  </TooltipHost>            
                  <TooltipHost content="Resend Notification" id='resendNotification' calloutProps={calloutProps} styles={hostStyles} >
                    <IconButton iconProps={SendIcon} aria-label="Resend Notification" onClick={() => this.props.resendNotifications(item.notificationId)} />
                  </TooltipHost>            
                  <TooltipHost content="Hide Notification" id='hideNotification' calloutProps={calloutProps} styles={hostStyles} >
                    <IconButton iconProps={HideIcon} aria-label="Hide Notification" onClick={() => this.props.hideRecord(item.notificationId)} />
                  </TooltipHost> 
                </div> 
              </div>
              <div className="notificationBody">{item.notificationBody}</div>
              <div className="notificationDate">{item.notificationDate}</div>
          </Stack.Item>
          </Stack>
        ))
        }
      </Stack>
    );
  }
}

I will create a short video and will add it to this post that will cover both the visualization and the notifications.

The source code of this control and the notifications can be downloaded from github