{"id":1470,"date":"2025-03-28T13:40:00","date_gmt":"2025-03-28T18:40:00","guid":{"rendered":"https:\/\/automatethemundane.com\/index.php\/2025\/03\/28\/how-to-turn-coe-data-into-diagrams-using-mermaid-and-power-automate\/"},"modified":"2025-03-31T06:57:01","modified_gmt":"2025-03-31T11:57:01","slug":"how-to-turn-coe-data-into-diagrams-using-mermaid-and-power-automate","status":"publish","type":"post","link":"https:\/\/automatethemundane.com\/index.php\/2025\/03\/28\/how-to-turn-coe-data-into-diagrams-using-mermaid-and-power-automate\/","title":{"rendered":"How to Turn COE Data into Diagrams Using Mermaid and Power Automate"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Goal<\/h1>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">When working with the Power Platform Center of Excellence (COE) toolkit, it's easy to end up with a lot of environment data \u2014 but not always a clear way to visualize it. I wanted a simple, dynamic way to turn that data into diagrams directly inside a model-driven app.<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">In this post, I\u2019ll walk through how I used Power Automate, a custom Dataverse table, and Mermaid.js to automatically generate and display environment diagrams. The result is a clean, interactive view of my Power Platform environments which updates as the data changes \u2014 and it only takes a few steps to set up.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddf1 Step 1: Create a New Solution\/Table to Represent Diagrams<\/h2>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">Because I do not want to lose all of this work when the next COE updates come out, all of the work will be done in a newly created solution\/table within the same environment as the COE install. See the diagram below to see how the table is shaped. <\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>erDiagram\n    DIAGRAM ||--|| ENVIRONMENT : references\n\n    DIAGRAM {\n        string DiagramName\n        string ActualDiagram\n    }\n\n    ENVIRONMENT {\n        string EnvironmentName\n    }\n\n<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Fields:\n<ul class=\"wp-block-list\">\n<li><strong>Diagram Name <\/strong>(Primary Name)<\/li>\n\n\n\n<li><strong>Actual Diagram<\/strong> (Rich Multiline Text)<\/li>\n\n\n\n<li>Lookup to COE Environment table (<code>environmentid<\/code>)<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Considerations:\n<ul class=\"wp-block-list\">\n<li>One record per diagram<\/li>\n\n\n\n<li>Storing rendered or raw Mermaid diagram syntax<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2699\ufe0f Step 2: Create Power Automate Logic to Build the Diagram<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li> The Flow will run once a day to pick up any new environments or update any existing ones. \n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-48.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n\n<li>Create seven variables. Each one is a string type\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-49.png\" alt=\"\"\/><\/figure>\n\n\n\n<ol class=\"wp-block-list\">\n<li>HTML Header\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n  &lt;body&gt;\n    &lt;pre class=\"mermaid\"&gt;<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n<\/li>\n\n\n\n<li>HTML Footer\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;\/pre&gt;\n    &lt;script type=\"module\"&gt;\n      import mermaid from 'https:\/\/cdn.jsdelivr.net\/npm\/mermaid@10\/dist\/mermaid.esm.min.mjs';\n    &lt;\/script&gt;\n  &lt;\/body&gt;\n&lt;\/html&gt;<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n<\/li>\n<\/ol>\n<\/li>\n\n\n\n<li>List rows from the COE Environments table that are active\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-50.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n\n<li>Create a scope for the single environment\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-51.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n\n<li>Add an Apply to each, for the List rows - COE Environments action\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-52.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n\n<li>Add a condition to check if a security group has been added to the environment\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-53-1024x512.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n\n<li>If it is blank, set the variables as follows\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-54.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n\n<li>If it is not blank, set them as follows \n\n\n\n\n\n\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-55.png\" alt=\"\"\/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n  subgraph s1&#91;\"@{variables('varTennant')}\"]\n    n2&#91;\"@{items('Apply_to_each')?&#91;'admin_securitygroupname']}\"]\n    n3&#91;\"@{items('Apply_to_each')?&#91;'admin_displayname']}\"]\n  end\n  n2 -- Access Control for --&gt; n3\n  ICON:n2:{ \"icon\": \"fluent-mdl2:security-group\", \"pos\": \"b\" }\n  ICON:n3:{ \"icon\": \"arcticons:microsoft-dynamics-365-remote-assist\", \"form\": \"rounded\", \"pos\": \"b\" }\n    style s1 fill:#FFFFFF,stroke:#D50000\n    style n2 fill:transparent\n    style n3 fill:#FFFFFF,stroke:none<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<p class=\"has-background\" style=\"background-color:rgb(241, 241, 239)\">\ud83d\udca1 This mermaid diagram is not \u201ccorrect\u201d because we need to escape the @{} within the mermaid so it will not be assumed it is an expression<\/p>\n<\/li>\n\n\n\n<li>Then, after the condition, list all current diagrams and filter it based upon the current environment \n\n\n\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-56.png\" alt=\"\"\/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>andy_diagramname eq '@{replace(items('Apply_to_each')?&#91;'admin_displayname'], '''', '''''')\n} - Diagram'<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n<\/li>\n\n\n\n<li>Another scope is added after the list rows to add the new diagram.<\/li>\n\n\n\n<li>Then check if the diagram is blank using the expression following expression\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-57-1024x332.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n\n<li>Use the following to get the diagram id. \n\n\n<pre class=\"wp-block-code\"><code>outputs('List_rows_-_Diagrams')?&#91;'body\/value']?&#91;0]?&#91;'andy_diagramid']<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n<\/li>\n\n\n\n<li>If it is blank, add a new row\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-58.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n\n<li>If the diagram is not blank, we  need to check if it has been updated. \n\n\n\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-59-1024x452.png\" alt=\"\"\/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>outputs('List_rows_-_Diagrams')?&#91;'body\/value']?&#91;0]?&#91;'andy_actualdiagram']<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n<\/li>\n\n\n\n<li>If it has not been updated, skip else update the diagram<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-60.png\" alt=\"\"\/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>outputs('List_rows_-_Diagrams')?&#91;'body\/value']?&#91;0]?&#91;'andy_diagramid']<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">&nbsp;<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd27 Step 3: Create the Web Resource to Render Mermaid<\/h3>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">To display our dynamically generated diagrams, we\u2019ll build a <strong>custom HTML + JavaScript web resource<\/strong> that renders Mermaid diagrams inside a Model-Driven App form. This allows you to visualize data directly from Dataverse using Mermaid.js and even include icons via Iconify packs.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udee0\ufe0f Web Resource Overview<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Type<\/strong>: HTML web resource (client-side)<\/li>\n\n\n\n<li><strong>JavaScript<\/strong>: ES Module syntax (using <code>type=\"module\"<\/code>)<\/li>\n\n\n\n<li><strong>Libraries<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Mermaid.js v11<\/li>\n\n\n\n<li>Icon packs via Iconify JSON CDN<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Purpose<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Fetch the diagram text from Dataverse<\/li>\n\n\n\n<li>Support Mermaid's new <code>@{ icon: ... }<\/code> annotation format<\/li>\n\n\n\n<li>Gracefully fallback if no diagram is available<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\uddf1 How It Works (Key Parts)<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 1. HTML Shell + Container<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>html\nCopyEdit\n&lt;body&gt;\n  &lt;div id=\"diagramContainer\"&gt;\n    &lt;pre id=\"diagramPre\" class=\"mermaid\"&gt;&lt;\/pre&gt;\n  &lt;\/div&gt;\n&lt;\/body&gt;\n\n<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">This is where the diagram is rendered. We use a <code>&lt;pre&gt;<\/code> tag with the <code>mermaid<\/code> class \u2014 Mermaid will detect and transform it into SVG.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 2. Import Mermaid &amp; Register Icon Packs<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>js\nCopyEdit\nimport mermaid from \"https:\/\/cdn.jsdelivr.net\/npm\/mermaid@11\/dist\/mermaid.esm.min.mjs\";\n\nmermaid.registerIconPacks(&#91;\n  {\n    name: \"logos\",\n    loader: () =&gt; fetch(\"https:\/\/unpkg.com\/@iconify-json\/logos@1\/icons.json\").then(res =&gt; res.json()),\n  },\n  {\n    name: \"fluent-mdl2\",\n    loader: () =&gt; fetch(\"https:\/\/unpkg.com\/@iconify-json\/fluent-mdl2@1\/icons.json\").then(res =&gt; res.json()),\n  }\n]);\n\nmermaid.initialize({ startOnLoad: false });\n\n<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We load Mermaid v11 as a module.<\/li>\n\n\n\n<li>Register any icon packs you want to use in your diagrams (<code>logos<\/code>, <code>fluent-mdl2<\/code>, etc.)<\/li>\n\n\n\n<li>Disable automatic rendering (<code>startOnLoad: false<\/code>) so we can manually trigger rendering after processing placeholders.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 3. Fetch Diagram Content from Dataverse<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>js\nCopyEdit\nfunction fetchAndDisplayDiagram() {\n  const formContext = window.parent.Xrm.Page;\n  const recordId = formContext.data.entity.getId().replace('{', '').replace('}', '');\n\n  const req = new XMLHttpRequest();\n  req.open(\"GET\", `${formContext.context.getClientUrl()}\/api\/data\/v9.0\/andy_diagrams(${recordId})?$select=andy_actualdiagram`, true);\n  req.setRequestHeader(\"Accept\", \"application\/json\");\n  req.setRequestHeader(\"Content-Type\", \"application\/json; charset=utf-8\");\n\n  req.onreadystatechange = function () {\n    if (this.readyState === 4 &amp;&amp; this.status === 200) {\n      const result = JSON.parse(this.response);\n      displayMermaidDiagram(result&#91;\"andy_actualdiagram\"]);\n    }\n  };\n\n  req.send();\n}\n\n<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">This pulls the Mermaid diagram from the <code>andy_actualdiagram<\/code> field on the record and passes it to our render function.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 4. Replace Icon Placeholders with Valid Syntax<\/h3>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">Because <code>@{ ... }<\/code> breaks Power Automate expressions, we use a safe placeholder in the flow like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>plaintext\nCopyEdit\nICON:n2:{ \"icon\": \"fluent-mdl2:security-group\", \"pos\": \"b\" }\n\n<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">Then in the web resource:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>js\nCopyEdit\nmermaidCode = mermaidCode.replace(\/ICON:(S+):({&#91;^}]+})\/g, 'n$1@$2n');\n\n<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">This transforms placeholders into valid Mermaid syntax like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mermaid\nCopyEdit\nn2@{ \"icon\": \"fluent-mdl2:security-group\", \"pos\": \"b\" }\n\n<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 5. Render the Diagram<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>js\nCopyEdit\npre.textContent = mermaidCode;\n\nmermaid.run().then(() =&gt; {\n  const svg = document.querySelector(\"#diagramContainer svg\");\n  if (svg) {\n    svg.removeAttribute(\"height\");\n    svg.removeAttribute(\"width\");\n    svg.style.width = \"100%\";\n    svg.style.height = \"100%\";\n  }\n});\n\n<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">This tells Mermaid to render the diagram and ensures the resulting SVG expands responsively.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 6. Handle Fallbacks Gracefully<\/h3>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">If no diagram is found, or the content only contains a <code>&lt;title&gt;<\/code> message:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>js\nCopyEdit\nconst titleMatch = \/&lt;title&gt;(.*?)&lt;\/title&gt;\/i.exec(htmlContent);\nconst fallbackMessage = titleMatch ? titleMatch&#91;1] : \"No diagram available.\";\n\ndocument.getElementById(\"diagramContainer\").innerHTML =\n  `&lt;div style=\"font-size: 1.2rem; color: #555;\"&gt;${fallbackMessage}&lt;\/div&gt;`;\n\n<\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">&nbsp;<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">Full code block<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html>\n&lt;html lang=\"en\">\n&lt;head>\n  &lt;meta charset=\"UTF-8\" \/>\n  &lt;title>Diagram Viewer&lt;\/title>\n  &lt;style>\n    html, body {\n      margin: 0;\n      padding: 0;\n      height: 100%;\n      width: 100%;\n      overflow: hidden;\n    }\n\n    body {\n      font-family: Arial, sans-serif;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      background-color: #f8f9fa;\n    }\n\n    #diagramContainer {\n      width: 100%;\n      height: 100%;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      overflow: auto;\n    }\n\n    pre.mermaid {\n      margin: 0;\n      width: 100%;\n      height: 100%;\n      display: block;\n    }\n  &lt;\/style>\n&lt;\/head>\n&lt;body>\n  &lt;div id=\"diagramContainer\">\n    &lt;pre id=\"diagramPre\" class=\"mermaid\">&lt;\/pre>\n  &lt;\/div>\n\n  &lt;script type=\"module\">\n    import mermaid from \"https:\/\/cdn.jsdelivr.net\/npm\/mermaid@11\/dist\/mermaid.esm.min.mjs\";\n\n    \/\/ Register icon packs\n    mermaid.registerIconPacks(&#91;\n      {\n        name: \"logos\",\n        loader: () =>\n          fetch(\"https:\/\/unpkg.com\/@iconify-json\/logos@1\/icons.json\").then((res) => res.json()),\n      },\n      {\n        name: \"arcticons\",\n        loader: () =>\n          fetch(\"https:\/\/unpkg.com\/@iconify-json\/arcticons@1\/icons.json\").then((res) => res.json()),\n      },\n      {\n        name: \"fluent-mdl2\",\n        loader: () =>\n          fetch(\"https:\/\/unpkg.com\/@iconify-json\/fluent-mdl2@1\/icons.json\").then((res) => res.json()),\n      },\n    ]);\n\n    mermaid.initialize({ startOnLoad: false });\n\n    function fetchAndDisplayDiagram() {\n      var formContext = window.parent.Xrm.Page;\n      var recordId = formContext.data.entity.getId().replace('{', '').replace('}', '');\n\n      var entitySetName = \"andy_diagrams\";\n      var columnName = \"andy_actualdiagram\";\n\n      var req = new XMLHttpRequest();\n      req.open(\n        \"GET\",\n        formContext.context.getClientUrl() +\n          \"\/api\/data\/v9.0\/\" +\n          entitySetName +\n          \"(\" +\n          recordId +\n          \")?$select=\" +\n          columnName,\n        true\n      );\n      req.setRequestHeader(\"OData-MaxVersion\", \"4.0\");\n      req.setRequestHeader(\"OData-Version\", \"4.0\");\n      req.setRequestHeader(\"Accept\", \"application\/json\");\n      req.setRequestHeader(\"Content-Type\", \"application\/json; charset=utf-8\");\n\n      req.onreadystatechange = function () {\n        if (this.readyState === 4) {\n          req.onreadystatechange = null;\n          if (this.status === 200) {\n            var result = JSON.parse(this.response);\n            var diagramHtml = result&#91;columnName];\n            displayMermaidDiagram(diagramHtml);\n          } else {\n            console.error(\"Error fetching diagram:\", this.responseText);\n          }\n        }\n      };\n      req.send();\n    }\n\n    function extractMermaidCode(html) {\n      const match = \/&lt;pre class=&#91;'\"]mermaid&#91;'\"]>(&#91;\\s\\S]*?)&lt;\\\/pre>\/.exec(html);\n      return match ? match&#91;1].trim() : null;\n    }\n\n    function displayMermaidDiagram(htmlContent) {\n      const pre = document.getElementById(\"diagramPre\");\n      let mermaidCode = extractMermaidCode(htmlContent);\n\n      if (mermaidCode) {\n        \/\/ Replace safe placeholders with real Mermaid icon syntax\n        mermaidCode = mermaidCode.replace(\/ICON:(\\S+):({&#91;^}]+})\/g, '\\n$1@$2\\n');\n\n        pre.textContent = mermaidCode;\n\n        mermaid.run().then(() => {\n          const svg = document.querySelector(\"#diagramContainer svg\");\n          if (svg) {\n            svg.removeAttribute(\"height\");\n            svg.removeAttribute(\"width\");\n            svg.style.width = \"100%\";\n            svg.style.height = \"100%\";\n            svg.style.maxWidth = \"100%\";\n            svg.style.maxHeight = \"100%\";\n          }\n        }).catch((err) => console.error(\"Mermaid render error:\", err));\n      } else {\n        const titleMatch = \/&lt;title>(.*?)&lt;\\\/title>\/i.exec(htmlContent);\n        const fallbackMessage = titleMatch ? titleMatch&#91;1] : \"No diagram available.\";\n        document.getElementById(\"diagramContainer\").innerHTML =\n          `&lt;div style=\"font-size: 1.2rem; color: #555;\">${fallbackMessage}&lt;\/div>`;\n      }\n    }\n\n    fetchAndDisplayDiagram();\n  &lt;\/script>\n&lt;\/body>\n&lt;\/html><\/code><\/pre>\n\n\n\n<p>&nbsp;<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\udde9 Step 4: Add the Web Resource to the Table Form<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Open the form editor for your custom diagram table<\/li>\n\n\n\n<li>Add a new iframe control:\n<ul class=\"wp-block-list\">\n<li>Link to the HTML web resource<\/li>\n\n\n\n<li>Set height (e.g., 8 rows)<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Optional: hide label and set border to off for a cleaner view\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-61-1024x647.png\" alt=\"\"\/><\/figure>\n<\/li>\n<\/ul>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">&nbsp;<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">If the diagram is blank, it will be rendered as follows \n\n<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-62-1024x435.png\" alt=\"\"\/><\/figure>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">If there is a diagram it will be shown<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/image-63-1024x440.png\" alt=\"\"\/><\/figure>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">&nbsp;<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">&nbsp;<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:rgb(0, 0, 0)\">The next part of this project will be to diagram the security of a solution and basic security roles (System Administrator) <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Goal When working with the Power Platform Center of Excellence (COE) toolkit, it&#8217;s easy to end up with a lot of environment data \u2014 but not always a clear way to visualize it. I wanted a simple, dynamic way to turn that data into diagrams directly inside a model-driven app. In this post, I\u2019ll walk [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1436,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[54,22,23,6,30,35],"tags":[],"class_list":["post-1470","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-coe","category-dataverse","category-forms","category-model-driven-app","category-power-automate","category-tables","entry","has-media"],"jetpack_featured_media_url":"https:\/\/automatethemundane.com\/wp-content\/uploads\/2025\/03\/photo-1550985543-49bee3167284-scaled.jpg","_links":{"self":[{"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/posts\/1470","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/comments?post=1470"}],"version-history":[{"count":2,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/posts\/1470\/revisions"}],"predecessor-version":[{"id":1473,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/posts\/1470\/revisions\/1473"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/media\/1436"}],"wp:attachment":[{"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/media?parent=1470"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/categories?post=1470"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/tags?post=1470"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}