Integrate the approval center into a model driven app/Dynamics 365 CE

Power Automate provides a standard solution to manage approvals in a Dataverse environment. Approval requests can be easily generated from cloud flows. Users have the ability to view and respond to approval requests from email, teams or the approval center which is accessible from the power automate website only.

Today, I will present a way to respond to approvals from a model driven app directly. Also, the approval requester will be able to cancel his request from the MDA in one click.


The approver can approve, reject, or assign the approval to a new approver in the organization:

The approval requestor can cancel his or her own approval request:


To implement this, we just need to use the widgets provided by the MsFlow js SDK. Yes, there is an SDK that allows us to generate some cool widgets. The approval center is one of them. Please refer to the documentation.

As the image above shows, I managed to integrate the approval center that is accessible from the power automate web app into a model driven app dashboard. Practically, my dashboard contains an HTML web resource. On this web page, I use the msflow js sdk to display my widget. This is the source code used

<html>
<head>
<title>Flow JS SDK Sample</title>
<style>
.flowContainer iframe {
border: none;
width: 100%;
height: 100%;
}
</style>
<script type="text/javascript" src="https://alcdn.msauth.net/browser/2.3.0/js/msal-browser.js"></script>
<script src="https://flow.microsoft.com/Content/msflowsdk-1.1.js"></script>
</head>
<body onfocusout="parent.setEmailRange();" style="overflow-wrap: break-word;">
<!–<button id="signOut" onclick="signOut()">SignOut</button>–>
<div id="flowsDiv" class="flowContainer"></div>
<script>
const msalConfig = {
auth: {
clientId: "e7c3a75a-a8ef-4406-b235-eb794e0d2c1c",
authority: "https://login.microsoftonline.com/4b63d104-ee12-4f0a-bb30-7a94a90f3103&quot;,
redirectUri: "https://yourOrg.crm4.dynamics.com/WebResources/mea_/Pages/FlowWidgetSPA.html&quot;,
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: true
},
flowScopes: ["https://service.flow.microsoft.com//Flows.Read.All&quot;, "https://service.flow.microsoft.com//Flows.Manage.All&quot;, "https://service.flow.microsoft.com//Approvals.Manage.All&quot;, "https://service.flow.microsoft.com//Approvals.Read.All&quot;]
};
var flowAccessToken;
var currentAccount;
const msalInstance = new msal.PublicClientApplication(msalConfig);
msalInstance.handleRedirectPromise().then(handleResponse).catch(err => {
console.error(err);
});
function handleResponse(resp) {
if (resp !== null) {
username = resp.account.username;
currentAccount = resp.account;
acquireTokenPopupAndLoadFlowsWidget();
} else {
const currentAccounts = msalInstance.getAllAccounts();
if (currentAccounts.length == 0) {
signIn();
} else if (currentAccounts.length > 1) {
// Add choose account code here
console.warn("Multiple accounts detected.");
} else if (currentAccounts.length === 1) {
username = currentAccounts[0].username;
console.log(currentAccounts[0]);
currentAccount = currentAccounts[0]
acquireTokenPopupAndLoadFlowsWidget();
}
}
}
function signIn() {
var loginRequest = {
scopes: msalConfig.flowScopes
}
msalInstance.loginPopup(loginRequest).then(handleResponse).catch(error => {
console.error(error);
});
}
function signOut() {
msalInstance.logout();
}
function acquireTokenPopupAndLoadFlowsWidget() {
const accessTokenRequest = {
scopes: msalConfig.flowScopes,
account: currentAccount
}
msalInstance.acquireTokenSilent(accessTokenRequest).then(function (accessToken) {
flowAccessTokenAcquired(accessToken);
}, function (error) {
console.log(error);
if (error.indexOf("consent_required") !== 1 || error.indexOf("interaction_required") !== 1 || error.indexOf("login_required") !== 1) {
msalInstance.acquireTokenPopup(msalConfig.flowScopes).then(function (accessToken) {
flowAccessTokenAcquired(accessToken);
}, function (error) {
console.log(error);
});
}
});
}
function flowAccessTokenAcquired(accessToken) {
flowAccessToken = accessToken.accessToken;
console.log(accessToken.accessToken);
loadApprovalsWidget();
}
function loadApprovalsWidget(accessToken) {
resetWidgetContainer();
var sdk = new window.MsFlowSdk({ hostName: window.location.origin, locale: 'en-us', hostId: window.WellKnownHostIds.WIDGETTEST });
var widget = sdk.renderWidget('approvalCenter', {
container: 'flowsDiv',
enableRegionalPortal: true,
enableOnBehalfOfTokens: true,
environmentId: '43391dad-6733-4eff-9765-027bedf0b244',
approvalCenterSettings: {
autoNavigateToDetails: true,
hideFlowCreation: true,
hideInfoPaneCloseButton: true,
hideLink: true,
showSimpleEmptyPage: true
}
});
widget.listen("GET_ACCESS_TOKEN", function (requestParam, widgetDoneCallback) {
widgetDoneCallback(null, {
token: flowAccessToken
});
});
widget.listen("WIDGET_READY", function () {
console.log("The flow widget is now ready.");
});
}
function resetWidgetContainer() {
var container = document.getElementById("flowsDiv");
while (container.firstChild) {
container.removeChild(container.firstChild);
}
}
</script>
</body>
</html>
view raw FlowWidgetSPA.html hosted with ❤ by GitHub

The Azure Active Directory App configuration:

Hope it helps…

7 thoughts on “Integrate the approval center into a model driven app/Dynamics 365 CE

  1. Hi Mehdi!

    Where did you get the “EnvironmentID” from listed at line: 106 in the code?
    As well as the authority id at the end of the URL at line: 23?

    Thanks for the help!

    Like

    1. Hi Johan,

      You will find your EnvironmentID by going through the following steps:

      Navigate to make.powerapps.com, then choose your environment. Now your URL will look like this: make.powerapps.com/environments/{EnvironmentID}/home

      the authority ID at the end of the URL at line: 23 is your tenantId

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s