<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Morgan Patou, auteur/autrice sur dbi Blog</title>
	<atom:link href="https://www.dbi-services.com/blog/author/morgan-patou/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.dbi-services.com/blog/author/morgan-patou/</link>
	<description></description>
	<lastBuildDate>Mon, 29 Jun 2026 20:41:39 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/cropped-favicon_512x512px-min-32x32.png</url>
	<title>Morgan Patou, auteur/autrice sur dbi Blog</title>
	<link>https://www.dbi-services.com/blog/author/morgan-patou/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>M-Files BD &#8211; Putting it together with the Visual Designer</title>
		<link>https://www.dbi-services.com/blog/m-files-bd-putting-it-together-with-the-visual-designer/</link>
					<comments>https://www.dbi-services.com/blog/m-files-bd-putting-it-together-with-the-visual-designer/#respond</comments>
		
		<dc:creator><![CDATA[Morgan Patou]]></dc:creator>
		<pubDate>Mon, 29 Jun 2026 20:19:33 +0000</pubDate>
				<category><![CDATA[Enterprise content management]]></category>
		<category><![CDATA[area]]></category>
		<category><![CDATA[Business Dashboard]]></category>
		<category><![CDATA[donut]]></category>
		<category><![CDATA[Gauge]]></category>
		<category><![CDATA[JSON Editor]]></category>
		<category><![CDATA[kpiNumber]]></category>
		<category><![CDATA[M-Files]]></category>
		<category><![CDATA[table]]></category>
		<category><![CDATA[Visual Designer]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=45433</guid>

					<description><![CDATA[<p>After several posts of theory, it&#8217;s time to build something. In this post, I will walk you through a Visual Designer tutorial to create a complete Contracts dashboard on the M-Files Sample Vault. Therefore, no JSON editing is required at all. The Visual Designer is the primary authoring path for most administrators. Power users who [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-putting-it-together-with-the-visual-designer/">M-Files BD &#8211; Putting it together with the Visual Designer</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">After several posts of theory, it&#8217;s time to build something. In this post, I will walk you through a Visual Designer tutorial to create a complete Contracts dashboard on the M-Files Sample Vault. Therefore, no JSON editing is required at all. The Visual Designer is the primary authoring path for most administrators. Power users who prefer hand-editing JSON can write directly in the JSON editor (toggle to switch in the top bar), but this post focuses on the visual approach where you build dashboards by clicking and filling forms.</p>



<p class="wp-block-paragraph">I picked the <strong>Sample Vault</strong> because it is the one that gets shipped when you are installing M-Files in evaluation mode and it contains some sample data, as its name suggests&#8230; Please note that since it is a rather old vault, data inside was created somewhere between 2004 and 2012. Therefore, in this blog, I will go back in time (yes, I can!) and consider that &#8220;today&#8221; is actually the &#8220;01-Nov-2011&#8221; (<strong>2011-11-01</strong>). In a real vault, you would use the dateToken <strong>&#8220;@today&#8221;</strong> instead, but for this blog, to allow you to reproduce the steps below, I chose that specific date since it gives interesting data to visualize.</p>



<h2 id="h-1-what-i-am-going-to-build" class="wp-block-heading">1. What I am going to build</h2>



<p class="wp-block-paragraph">The target dashboard has eight widgets:</p>



<ul class="wp-block-list">
<li>Four KPI tiles in a row at the top: Active Contracts, Expired Contracts, Contract Expiry &lt; 180d, No expiry date.</li>



<li>One donut: Contracts by agreement type.</li>



<li>One gauge (date-mode): days until the earliest/next contract expiry.</li>



<li>One area: contract expiry per year and per agreement type.</li>



<li>One table: all expired contracts (for review or sales purpose).</li>
</ul>



<p class="wp-block-paragraph">This layout fills the right pane with a clean &#8220;summary at the top, distribution in the middle, action items at the bottom&#8221; reading order.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1212c492&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1212c492" class="wp-block-image size-full wp-lightbox-container"><img fetchpriority="high" decoding="async" width="2560" height="2011" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.1-scaled.png" alt="Visual Designer tutorial end result" class="wp-image-45434" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.1-scaled.png 2560w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.1-300x236.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.1-1024x804.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.1-768x603.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.1-1536x1206.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.1-2048x1609.png 2048w" sizes="(max-width: 2560px) 100vw, 2560px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-2-creating-a-new-dashboard" class="wp-block-heading">2. Creating a new dashboard</h2>



<p class="wp-block-paragraph">The dashboard management is done on the M-Files Admin UI. To access it, open <strong>M-Files Admin</strong> -&gt; <strong>Configurations</strong> -&gt; <strong>Other Applications</strong> -&gt; <strong>dbi services Business Dashboard</strong> -&gt; <strong>Dashboard</strong> tab. Then click on the <strong><em>Add New</em></strong> button. The editor opens, by default in Visual mode with an empty canvas and the Dashboard Settings panel on the right:</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1212cc0a&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1212cc0a" class="wp-block-image size-full wp-lightbox-container"><img decoding="async" width="2370" height="1668" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.2.png" alt="Visual Designer default look&amp;feel" class="wp-image-45435" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.2.png 2370w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.2-300x211.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.2-1024x721.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.2-768x541.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.2-1536x1081.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.2-2048x1441.png 2048w" sizes="(max-width: 2370px) 100vw, 2370px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-3-dashboard-settings" class="wp-block-heading">3. Dashboard Settings</h2>



<p class="wp-block-paragraph">Before adding widgets, let&#8217;s configure the dashboard-level fields in the right panel. The below assumes you never updated the default values. If you would like to update the defaults so all future dashboards have your own, you can do it in the <strong>Configuration</strong> tab.</p>



<ul class="wp-block-list">
<li><strong>Name</strong>: <strong><em>Contracts Snapshot (01-Nov-2011)</em></strong>.</li>



<li><strong>Description (optional)</strong>: <strong><em>Quick view of contracts: active, expiring, distribution.</em></strong>.</li>



<li><strong>Auto-refresh enabled</strong>: <strong><em>No</em></strong> (leave off for now, you can flip it on later if needed).</li>



<li><strong>User can toggle</strong>: <strong><em>Yes</em></strong> (users can enable the auto-refresh).</li>



<li><strong>Export to PDF enabled</strong>: <strong><em>Yes</em></strong>.</li>



<li><strong>Export to CSV enabled</strong>: <strong><em>Yes</em></strong>.</li>



<li><strong>Drill-through enabled</strong>: <strong><em>Yes</em></strong> (this is what makes the chart elements clickable and it displays details about the matching objects).</li>



<li>Leave the rest as default. There are enough query results to fetch all details, each user sees only the objects their M-Files ACL allows, etc.</li>
</ul>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1212d42b&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1212d42b" class="wp-block-image size-full wp-lightbox-container"><img decoding="async" width="2366" height="1666" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.3.png" alt="Updating the dashboard parameters in the Visual Designer" class="wp-image-45436" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.3.png 2366w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.3-300x211.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.3-1024x721.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.3-768x541.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.3-1536x1082.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.3-2048x1442.png 2048w" sizes="(max-width: 2366px) 100vw, 2366px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-4-the-first-kpi-tile-active-contracts" class="wp-block-heading">4. The first KPI tile: Active Contracts</h2>



<p class="wp-block-paragraph">Click on the <strong><em>+ Add Widget</em></strong> button in the footer. A blank tile appears on the canvas and the right panel switches to the widget config.</p>



<p class="wp-block-paragraph">First, we set the widget basic details:</p>



<ul class="wp-block-list">
<li><strong>Widget Title</strong>: <strong><em>Active Contracts</em></strong>.</li>



<li><strong>Description (optional)</strong>: leave empty.</li>



<li><strong>Widget Type</strong>: <strong><em>KPI Number</em></strong>.</li>



<li><strong>Col Span</strong>: <strong><em>3</em></strong>.</li>



<li><strong>Row Span</strong>: <strong><em>1</em></strong>.</li>
</ul>



<p class="wp-block-paragraph">Then, we need to configure the source of the data we want to see:</p>



<ul class="wp-block-list">
<li><strong>Object Type(s)</strong>: <strong><em>Document</em></strong> (dropdown shows the vault&#8217;s object types).</li>



<li><strong>Class (optional)</strong>: <strong><em>Contract or Agreement</em></strong> (automatically populated after selecting an object type).</li>



<li><strong>Aggregation Type</strong>: <strong><em>summary</em></strong> (already set automatically since it&#8217;s the only one supported by kpiNumber).</li>
</ul>



<p class="wp-block-paragraph">Optionally, we can configure one or more filters. Here, since we want to see only active contracts (i.e. that have an effective-through date superior or equal to today), then we need to apply a certain filter. Click on the <strong><em>+ Add Filter</em></strong> button and a row appears:</p>



<ul class="wp-block-list">
<li><strong>Property</strong>: <strong><em>Effective through (date)</em></strong> (from the property dropdown, the &#8220;(date)&#8221; is the data-type that is shown but it&#8217;s not part of the property name).</li>



<li><strong>Operator</strong>: <strong><em>greater than or equal (&gt;=)</em></strong>.</li>



<li><strong>Value type</strong>: <strong><em>dateToken</em></strong>.</li>



<li><strong>Value</strong>: <strong><em>2011-11-01</em></strong> (instead of &#8220;@today&#8221; in a real vault).</li>
</ul>



<p class="wp-block-paragraph">Finally, we define the aggregation (i.e. what to do with the results obtained) as well as the display section, which are optional graphical/rendering details:</p>



<ul class="wp-block-list">
<li><strong>Reducer</strong>: <strong><em>count</em></strong> (default).</li>



<li><strong>Drill-through Columns (comma-sep)</strong>: <strong><em>Agreement type, Effective through</em></strong> (the additional columns to add to table lists or drill-through details tables).</li>



<li><strong>Display Unit</strong>: <strong><em>contracts</em></strong>.</li>
</ul>



<p class="wp-block-paragraph">To validate that the configuration is correct, you can click on the <strong><em>Test Widget</em></strong> button in the footer. The Test Results panel expands on the right side, and shows a green <strong><em>OK</em></strong>, which confirms the definition is correct. So far, so good.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1212dfd1&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1212dfd1" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="2368" height="1664" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.4.png" alt="Testing a first widget in Visual Designer" class="wp-image-45437" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.4.png 2368w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.4-300x211.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.4-1024x720.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.4-768x540.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.4-1536x1079.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.4-2048x1439.png 2048w" sizes="auto, (max-width: 2368px) 100vw, 2368px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-5-the-other-three-kpi-tiles" class="wp-block-heading">5. The other three KPI tiles</h2>



<p class="wp-block-paragraph">The next three KPIs follow the same pattern as the first one, with different filters.</p>



<h3 id="h-5-1-expired-contracts" class="wp-block-heading">5.1. Expired Contracts</h3>



<p class="wp-block-paragraph">With the 1st widget selected, click on the <strong><em>Clone Widget</em></strong> button. A new tile appears next to the first one, with exactly the same content. Simply update these fields:</p>



<ul class="wp-block-list">
<li><strong>Widget Title</strong>: <strong><em>Expired Contracts</em></strong>.</li>



<li><strong>Operator</strong> (Filter): <strong><em>less than (&lt;)</em></strong>.</li>
</ul>



<p class="wp-block-paragraph">Click on the <strong><em>+ Add Threshold</em></strong> button and a row appears:</p>



<ul class="wp-block-list">
<li><strong>Value</strong> (Threshold): <strong><em>1</em></strong> (any value equal or above 1 will match).</li>



<li><strong>Color</strong> (Threshold): <strong><em>red</em></strong> or <strong><em>#e74c3c</em></strong> (color used if above value is reached).</li>
</ul>



<h3 id="h-5-2-expiring-in-next-180-days" class="wp-block-heading">5.2. Expiring in next 180 days</h3>



<p class="wp-block-paragraph">Click on the <strong><em>Clone Widget</em></strong> button again:</p>



<ul class="wp-block-list">
<li><strong>Widget Title</strong>: <strong><em>Contract Expiry &lt; 180d</em></strong>.</li>



<li><strong>Operator</strong> (Filter): <strong><em>between (from,to)</em></strong></li>



<li><strong>Value</strong> (Filter): <strong><em>2011-11-01, 2011-11-01+180d</em></strong> (instead of &#8220;@today, @today+180d&#8221; in a real vault).</li>



<li><strong>Color</strong> (Threshold): <strong><em>orange</em></strong> or <strong><em>#f39c12</em></strong> (update the existing threshold and keep the value as 1).</li>
</ul>



<h3 id="h-5-3-no-expiry-date" class="wp-block-heading">5.3. No expiry date</h3>



<p class="wp-block-paragraph">Click on the <strong><em>Clone Widget</em></strong> button again:</p>



<ul class="wp-block-list">
<li><strong>Widget Title</strong>: <strong><em>No expiry date</em></strong>.</li>



<li><strong>Operator</strong> (Filter): <strong><em>is empty (incl. absent)</em></strong> (no value field for this operator).</li>
</ul>



<p class="wp-block-paragraph">Click on the <strong><em>+ Add Threshold</em></strong> button and a second row appears (keep the other one with 1-orange):</p>



<ul class="wp-block-list">
<li><strong>Value</strong> (Threshold): <strong><em>0</em></strong> (any value equal or above 0 but below 1 (c.f. other threshold) will match. i.e. only valid if equal to 0).</li>



<li><strong>Color</strong> (Threshold): <strong><em>green</em></strong> or <strong><em>#27ae60</em></strong> (color used if above value is reached).</li>
</ul>



<p class="wp-block-paragraph">At this point the canvas shows four KPI tiles. They each have <strong><em>colSpan: 3</em></strong>, so they sit nicely in a row of four totalling 12 (full width of the dashboard). You can click on <strong><em>Test Queries</em></strong> button (at the top) to validate that all four kpiNumber widgets are correct.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1212ed2c&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1212ed2c" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="2368" height="1668" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.5.png" alt="Testing the first row widgets in Visual Designer" class="wp-image-45438" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.5.png 2368w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.5-300x211.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.5-1024x721.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.5-768x541.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.5-1536x1082.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.5-2048x1443.png 2048w" sizes="auto, (max-width: 2368px) 100vw, 2368px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-6-the-donut-contracts-by-agreement-type" class="wp-block-heading">6. The donut: contracts by agreement type</h2>



<p class="wp-block-paragraph">Click on the <strong><em>+ Add Widget</em></strong> button in the footer to add a new widget:</p>



<ul class="wp-block-list">
<li><strong>Widget Title</strong>: <strong><em>By Agreement Type</em></strong>.</li>



<li><strong>Widget Type</strong>: <strong><em>Donut Chart</em></strong>.</li>



<li><strong>Col Span</strong>: <strong><em>6</em></strong>.</li>



<li><strong>Row Span</strong>: <strong><em>2</em></strong> (chart widgets need height, see Post 3).</li>



<li><strong>Object Type(s)</strong>: <strong><em>Document</em></strong>.</li>



<li><strong>Class (optional)</strong>: <strong><em>Contract or Agreement</em></strong>.</li>



<li><strong>Aggregation Type</strong>: <strong><em>Group by Property</em></strong> (all objects with the same value of a specific property will be grouped together).</li>



<li><strong>Group-by Property</strong>: <strong><em>Agreement type (lookup)</em></strong> (the property to be used for the grouping).</li>



<li><strong>Drill-through Columns (comma-sep)</strong>: <strong><em>Agreement type, Effective through</em></strong>.</li>
</ul>



<h2 id="h-7-the-date-mode-gauge-countdown-to-next-expiry" class="wp-block-heading">7. The date-mode gauge: countdown to next expiry</h2>



<p class="wp-block-paragraph">The gauge is where things get more interesting. Here, I would like a dial showing how many days are left until the earliest/next contract expiry. This can be useful for making sure you never reach &#8220;0&#8221; (i.e. the date of today) on some tasks, like contracts renewal, period reviews of some documents, etc. You can see this as something similar to a fuel gauge in a car. If it reaches the minimum, you might be in trouble&#8230; The reducer is <strong><em>min</em></strong> on the <strong><em>Effective through</em></strong> property, and since this property is a date, it triggers date-mode automatically.</p>



<p class="wp-block-paragraph">Add the sixth widget. You know the drill, <strong><em>+ Add Widget</em></strong>:</p>



<ul class="wp-block-list">
<li><strong>Widget Title</strong>: <strong><em>Closest Expiry</em></strong>.</li>



<li><strong>Widget Type</strong>: <strong><em>Gauge</em></strong>.</li>



<li><strong>Col Span</strong>: <strong><em>6</em></strong>.</li>



<li><strong>Row Span</strong>: <strong><em>2</em></strong>.</li>



<li><strong>Object Type(s)</strong>: <strong><em>Document</em></strong>.</li>



<li><strong>Class (optional)</strong>: <strong><em>Contract or Agreement</em></strong>.</li>
</ul>



<p class="wp-block-paragraph">Let&#8217;s add a filter to keep only valid contracts again: click on the <strong><em>+ Add Filter</em></strong> button and a row appears:</p>



<ul class="wp-block-list">
<li><strong>Property</strong>: <strong><em>Effective through (date)</em></strong>.</li>



<li><strong>Operator</strong>: <strong><em>greater than or equal (&gt;=)</em></strong>.</li>



<li><strong>Value type</strong>: <strong><em>dateToken</em></strong>.</li>



<li><strong>Value</strong>: <strong><em>2011-11-01</em></strong> (instead of &#8220;@today&#8221; in a real vault).</li>
</ul>



<ul class="wp-block-list">
<li><strong>Reducer</strong>: <strong><em>min</em></strong>.</li>



<li><strong>Reducer Property</strong>: <strong><em>Effective through (date)</em></strong>.</li>



<li><strong>Drill-through Columns (comma-sep)</strong>: <strong><em>Agreement type, Effective through</em></strong>.</li>
</ul>



<p class="wp-block-paragraph">Now let&#8217;s add a first threshold: click on the <strong><em>+ Add Threshold</em></strong> button and a row appears:</p>



<ul class="wp-block-list">
<li><strong>Value</strong> (Threshold): <strong><em>0</em></strong> (so &#8220;today&#8221; = the contract expiry is very soon).</li>



<li><strong>Color</strong> (Threshold): <strong><em>red</em></strong> or <strong><em>#e74c3c</em></strong>.</li>
</ul>



<p class="wp-block-paragraph">Click on the <strong><em>+ Add Threshold</em></strong> button and a second row appears:</p>



<ul class="wp-block-list">
<li><strong>Value</strong> (Threshold): <strong><em>40</em></strong> (so &#8220;next month&#8221; = the contract expiry is coming).</li>



<li><strong>Color</strong> (Threshold): <strong><em>orange</em></strong> or <strong><em>#f39c12</em></strong>.</li>
</ul>



<p class="wp-block-paragraph">Click on the <strong><em>+ Add Threshold</em></strong> button and a third row appears:</p>



<ul class="wp-block-list">
<li><strong>Value</strong> (Threshold): <strong><em>80</em></strong> (so at least &#8220;2 months left&#8221; = the contract expiry is still far).</li>



<li><strong>Color</strong> (Threshold): <strong><em>green</em></strong> or <strong><em>#27ae60</em></strong>.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Display Min (day offset)</strong>: <strong><em>0</em></strong> (so &#8220;today&#8221;).</li>



<li><strong>Display Max (day offset)</strong>: <strong><em>180</em></strong> (so &#8220;today&#8221; + 180 days).</li>



<li><strong>Display Unit</strong>: <strong><em>next expiry</em></strong>.</li>
</ul>



<h3 id="h-7-1-sample-vault-date-limitation" class="wp-block-heading">7.1. Sample Vault date limitation</h3>



<p class="wp-block-paragraph"><strong>Note:</strong> This widget will not fully work on the Sample Vault as-is, since the data is from 2011 and all contracts are already in the past. In a real vault, you would use <strong><em>@today</em></strong> as the filter value and set min/max to 0 and 180. The gauge would then show the number of days until the earliest active contract expires.</p>



<p class="wp-block-paragraph">On the Sample Vault, since 01-Nov-2011 is roughly 5350 days in the past, you would need to set min to &#8220;-5350&#8221; and max to &#8220;-5350+180&#8221; to see a meaningful result. The exact offset depends on when you read this, which is precisely why date tokens like <strong><em>@today</em></strong> exist &#8211; they adapt automatically.</p>



<p class="wp-block-paragraph">If I set the min/max offset to the accurate number of days since 01-Nov-2011, the gauge looks like this for end-users (that&#8217;s what It will look like on a real vault with <strong><em>@today</em></strong> and 0-180 values):</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1212fe81&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1212fe81" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="844" height="492" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.7.png" alt="Gauge in date-mode" class="wp-image-45439" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.7.png 844w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.7-300x175.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.7-768x448.png 768w" sizes="auto, (max-width: 844px) 100vw, 844px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-8-the-area-chart-expiry-per-year-and-per-agreement-type" class="wp-block-heading">8. The area chart: expiry per year and per agreement type</h2>



<p class="wp-block-paragraph">Add the seventh widget. It didn&#8217;t change since earlier, it&#8217;s still <strong><em>+ Add Widget</em></strong>&#8230;</p>



<ul class="wp-block-list">
<li><strong>Widget Title</strong>: <strong><em>Expiry per Year and Agreement Type</em></strong>.</li>



<li><strong>Widget Type</strong>: <strong><em>Area Chart</em></strong>.</li>



<li><strong>Col Span</strong>: <strong><em>12</em></strong>.</li>



<li><strong>Row Span</strong>: <strong><em>2</em></strong>.</li>



<li><strong>Object Type(s)</strong>: <strong><em>Document</em></strong>.</li>



<li><strong>Class (optional)</strong>: <strong><em>Contract or Agreement</em></strong>.</li>



<li><strong>Aggregation Type</strong>: <strong><em>Group by Date Bucket</em></strong> (all objects with the same date bucket for a specific property will be grouped together).</li>



<li><strong>Date Property</strong>: <strong><em>Effective through (date)</em></strong> (the property to be used for the grouping).</li>



<li><strong>Bucket Size</strong>: <strong><em>year</em></strong> (the objects within the same year will be in the same group).</li>



<li><strong>Series Property (optional)</strong>: <strong><em>Agreement type (lookup)</em></strong> (a second grouping level).</li>



<li><strong>Drill-through Columns (comma-sep)</strong>: <strong><em>Agreement type, Effective through</em></strong>.</li>



<li><strong>Smooth Lines</strong>: <strong><em>Yes</em></strong>.</li>
</ul>



<p class="wp-block-paragraph"><strong>Note:</strong> Instead of an <strong><em>Area Chart</em></strong>, you can use a <strong><em>Line Chart</em></strong> or <strong><em>Bar Chart</em></strong>, these three are usually interchangeable and can give the same amount of information visually.</p>



<h2 id="h-9-the-table-expired-contracts-to-review" class="wp-block-heading">9. The table: expired contracts to review</h2>



<p class="wp-block-paragraph">The last widget is a table showing the list of all expired contracts. This can be used for review or for some sales purpose, as previously mentioned. So let&#8217;s <strong><em>+ Add Widget</em></strong> one last time:</p>



<ul class="wp-block-list">
<li><strong>Widget Title</strong>: <strong><em>Expired Contracts to Review</em></strong>.</li>



<li><strong>Widget Type</strong>: <strong><em>Table</em></strong>.</li>



<li><strong>Col Span</strong>: <strong><em>12</em></strong>.</li>



<li><strong>Row Span</strong>: <strong><em>2</em></strong> (you can set 3 as well, for bigger table/lists).</li>



<li><strong>Object Type(s)</strong>: <strong><em>Document</em></strong>.</li>



<li><strong>Class (optional)</strong>: <strong><em>Contract or Agreement</em></strong>.</li>



<li><strong>Aggregation Type</strong>: <strong><em>List (list of individual objects)</em></strong>.</li>
</ul>



<p class="wp-block-paragraph">Click on the <strong><em>+ Add Filter</em></strong> button and a row appears:</p>



<ul class="wp-block-list">
<li><strong>Property</strong>: <strong><em>Effective through (date)</em></strong>.</li>



<li><strong>Operator</strong>: <strong><em>less than (&lt;)</em></strong>.</li>



<li><strong>Value type</strong>: <strong><em>dateToken</em></strong>.</li>



<li><strong>Value</strong>: <strong><em>2011-11-01</em></strong> (instead of &#8220;@today&#8221; in a real vault).</li>
</ul>



<ul class="wp-block-list">
<li><strong>Display Properties (comma-sep)</strong>: <strong><em>Agreement type, Effective through, Owner</em></strong>.</li>
</ul>



<h2 id="h-10-sanity-check-amp-saving" class="wp-block-heading">10. Sanity check &amp; Saving</h2>



<p class="wp-block-paragraph">Before saving, you can click on the <strong><em>Test Queries</em></strong> button from the toolbar to validate all widgets at once. The right-panel should list eight widgets, all green and without any warnings or errors. If something is wrong &#8211; a property name typo, an incompatible reducer / widget combination &#8211; this is where it would surface, with a clean message describing what to fix.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab12130bfd&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab12130bfd" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="2370" height="1668" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.10a.png" alt="Final result of the full dashboard and validation test successful" class="wp-image-45440" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.10a.png 2370w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.10a-300x211.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.10a-1024x721.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.10a-768x541.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.10a-1536x1081.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.10a-2048x1441.png 2048w" sizes="auto, (max-width: 2370px) 100vw, 2370px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<p class="wp-block-paragraph">If it&#8217;s all green, then click on the <strong><em>Save</em></strong> button in the toolbar. The editor should display a message at the bottom saying it validated everything and then closes (you need to be quick to see the message). The Admin UI Dashboard tab refreshes, and the Contracts Snapshot dashboard now appears in the list, with its number of widgets and its description.</p>



<p class="wp-block-paragraph">Now, let&#8217;s see what it looks like from an end-user perspective after all that work! You can open the M-Files Web or Desktop (or do a refresh CTRL+F5 / CMD+R if it&#8217;s already loaded), open the right pane by clicking on the <strong>Dashboard</strong> tab, and pick <strong>Contracts Snapshot</strong> from the dropdown (it should be displayed automatically if it&#8217;s your first dashboard). Every chart element should be clickable (drill-through is enabled), and the PDF / CSV export buttons should also be present.</p>



<p class="wp-block-paragraph">See the first screenshot at the beginning of this blog post to see the final result that you can expect if you followed along the steps so far. Still, since you made it this far, here is what it looks like as a PDF Export:</p>



<div data-wp-interactive="core/file" class="wp-block-file"><object data-wp-bind--hidden="!state.hasPdfPreview" hidden class="wp-block-file__embed" data="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.10b.pdf" type="application/pdf" style="width:100%;height:840px" aria-label="Embed of Contracts Snapshot (01-Nov-2011)."></object><a id="wp-block-file--media-76220328-afb2-4a60-86cd-f639a9c0350c" href="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.10b.pdf">Contracts Snapshot (01-Nov-2011)</a><a href="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/8.10b.pdf" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-76220328-afb2-4a60-86cd-f639a9c0350c">Download</a></div>



<h2 id="h-11-closing-thoughts" class="wp-block-heading">11. Closing thoughts</h2>



<p class="wp-block-paragraph">I initially created the <strong>Business Dashboard</strong> with the JSON editor only, but I always had in mind that I needed to add a Visual Designer at some point, because not everybody knows how (or wants) to write JSON. Also, writing JSON directly requires you to know what is required, optional, supported or not supported. Even if the <strong><em>Test Queries</em></strong> and <strong><em>Save</em></strong> buttons both execute multiple layers of validations, to ensure everything you enter is supported, it still sends you warning after warning, error after error if you do not really know what you are doing. Therefore, the Visual Designer is great for that purpose, because you just click-through the details and you are guided all the way.</p>



<p class="wp-block-paragraph">The Visual / JSON toggle at the top of the editor lets you switch between authoring paths at any time, and your work is preserved in both directions. If you prefer hand-editing JSON or need to do bulk changes, the JSON mode is always available—but most administrators find the Visual Designer sufficient for their day-to-day work.</p>



<p class="wp-block-paragraph">Want to know more about this Business Dashboard? <a href="https://www.dbi-services.com/company/contact/" target="_blank" rel="noreferrer noopener">Contact us</a> and we will be happy to showcase it on <a href="https://www.m-files.com/" target="_blank" rel="noreferrer noopener">M-Files</a>.</p>



<p class="wp-block-paragraph">Quick access to previous blog posts of the series:</p>



<ul class="wp-block-list">
<li><strong><a href="https://www.dbi-services.com/blog/m-files-introducing-the-business-dashboard-module/" id="44913" target="_blank" rel="noreferrer noopener">Post 1</a></strong> &#8211; Introduction to the <strong>Business Dashboard</strong>.</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-end-user-experience/" id="44932" target="_blank" rel="noreferrer noopener">Post 2</a></strong> &#8211; The <strong>end-user experience</strong>: opening the panel, switching dashboards, drill-through, exports, favorites, sharing.</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-anatomy-of-a-dashboard-definition/" id="44952" target="_blank" rel="noreferrer noopener">Post 3</a></strong> &#8211; The anatomy of a <strong>dashboard definition</strong>: the JSON structure that drives everything.</li>



<li><strong>Posts <a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/" id="45018" target="_blank" rel="noreferrer noopener">4a</a>, <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" id="45076" target="_blank" rel="noreferrer noopener">4b</a>, <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/" id="45109" target="_blank" rel="noreferrer noopener">4c</a></strong> &#8211; The <strong>widget catalog</strong>, split by type family: scalar widgets (<strong><em>kpiNumber</em></strong>, <strong><em>gauge</em></strong>), trend widgets (<strong><em>line</em></strong>, <strong><em>area</em></strong>), and distribution / tabular widgets (<strong><em>donut</em></strong>, <strong><em>bar</em></strong>, <strong><em>table</em></strong>).</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/" id="45175" target="_blank" rel="noreferrer noopener">Post 5</a></strong> &#8211; Writing queries: <strong><em>objectType</em></strong>, <strong><em>class</em></strong>, <strong>filters</strong>, date tokens.</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/" id="45206" target="_blank" rel="noreferrer noopener">Post 6</a></strong> &#8211; <strong>Aggregations</strong> and <strong>reducers</strong>.</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-the-admin-part/" id="45312" target="_blank" rel="noreferrer noopener">Post 7</a></strong> &#8211; The <strong>Admin tab</strong>: actions, Visual Designer / JSON editor, validation, import / export, access control.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-putting-it-together-with-the-visual-designer/">M-Files BD &#8211; Putting it together with the Visual Designer</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/m-files-bd-putting-it-together-with-the-visual-designer/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>M-Files BD &#8211; The Admin part</title>
		<link>https://www.dbi-services.com/blog/m-files-bd-the-admin-part/</link>
					<comments>https://www.dbi-services.com/blog/m-files-bd-the-admin-part/#respond</comments>
		
		<dc:creator><![CDATA[Morgan Patou]]></dc:creator>
		<pubDate>Thu, 25 Jun 2026 23:29:53 +0000</pubDate>
				<category><![CDATA[Enterprise content management]]></category>
		<category><![CDATA[Admin]]></category>
		<category><![CDATA[Business Dashboard]]></category>
		<category><![CDATA[JSON Editor]]></category>
		<category><![CDATA[M-Files]]></category>
		<category><![CDATA[Visual Designer]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=45312</guid>

					<description><![CDATA[<p>So far in this series I have covered what an end user sees, the anatomy of a dashboard definition, the seven widget types (scalar, trend, and the rest), the query side, including the aggregations. Time to look at the surface where the administrator actually spends time: the Dashboard tab in M-Files Admin. This is also [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-the-admin-part/">M-Files BD &#8211; The Admin part</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">So far in this series I have covered what an <a href="https://www.dbi-services.com/blog/m-files-bd-end-user-experience/" id="44932" target="_blank" rel="noreferrer noopener">end user sees</a>, the <a href="https://www.dbi-services.com/blog/m-files-bd-anatomy-of-a-dashboard-definition/" id="44952" target="_blank" rel="noreferrer noopener">anatomy of a dashboard definition</a>, the seven widget types (<a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/" id="45018" target="_blank" rel="noreferrer noopener">scalar</a>, <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" id="45076" target="_blank" rel="noreferrer noopener">trend</a>, and <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/" id="45109" target="_blank" rel="noreferrer noopener">the rest</a>), the <a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/" id="45175" target="_blank" rel="noreferrer noopener">query side</a>, including the <a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/" id="45206" target="_blank" rel="noreferrer noopener">aggregations</a>. Time to look at the surface where the administrator actually spends time: the <strong>Dashboard tab in M-Files Admin</strong>.</p>



<p class="wp-block-paragraph">This is also where the Visual Designer lives. Some administrators write JSON directly, others stay in the visual canvas, others switch back and forth. All three workflows are supported, and the two modes are kept in sync at every moment.</p>



<h2 id="h-1-where-to-find-the-admin-tab" class="wp-block-heading">1. Where to find the Admin tab</h2>



<p class="wp-block-paragraph">Once it has been installed (e.g. via <strong>Vault</strong> -&gt; Right click -&gt; <strong>Applications</strong> -&gt; <strong>Install&#8230;</strong>), the Business Dashboard will be present in the M-Files Admin configuration page:</p>



<ol class="wp-block-list">
<li>Open <strong>M-Files Admin</strong> (the desktop management application).</li>



<li>Connect to your server and open the target vault.</li>



<li>In the left tree, navigate to <strong>Configurations</strong> -&gt; <strong>Other Applications</strong>.</li>



<li>Find <strong>dbi services Business Dashboard</strong> and click it.</li>



<li>You should end up on the <strong>Dashboard</strong> tab.</li>
</ol>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab12137a36&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab12137a36" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="2560" height="1418" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1-scaled.png" alt="Business Dashboard Admin part" class="wp-image-45313" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1-scaled.png 2560w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1-300x166.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1-1024x567.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1-768x425.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1-1536x851.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1-2048x1134.png 2048w" sizes="auto, (max-width: 2560px) 100vw, 2560px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<p class="wp-block-paragraph">The first thing you will probably want to do is to look at the <strong>Configuration</strong> tab, that sits right next to the <strong>Dashboard</strong> one. This is where you can configure the <strong>Business Dashboard</strong> as needed.</p>



<h3 id="h-1-1-the-configuration-tab-general-settings" class="wp-block-heading">1.1. The Configuration tab &#8211; &#8220;General&#8221; settings</h3>



<p class="wp-block-paragraph">At the time of writing this blog, the <strong>General</strong> section of the configuration is rather short. It only contains 4 settings:</p>



<ul class="wp-block-list">
<li><strong><em>Table page size</em></strong>: controls the pagination of all tables (list &amp; drill-through modals), i.e. the maximum number of rows that can be displayed before a new page is required. (default: 15).</li>



<li><strong><em>Default widget color</em></strong>: controls the base color of the widgets (end-user side), like the number inside the kpiNumber, the gauge and its pointer, the table header, the line and area. It&#8217;s only the base color, so it still gets updated/overwritten by the display.thresholds parameter of each widget. (default: #006eef &#8211; the M-Files &#8220;standard&#8221; blue).</li>



<li><strong><em>Default chrome color</em></strong>: controls the base color for the &#8220;chrome&#8221; things (end-user side), for example the links shown inside the tables, to navigate to objects, as well as the spinner when dashboards/widgets are loading. (default: #006eef).</li>



<li><strong><em>Admin UI accent color</em></strong>: controls the base color for the buttons (admin side), like Save, Test Queries, etc. (default: #006eef).</li>
</ul>



<p class="wp-block-paragraph">Exactly like any other M-Files configuration items, as soon as you modify one of these values, the &#8220;Save&#8221; and &#8220;Discard&#8221; buttons appear so you can either persist and remove what you just changed.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab12138193&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab12138193" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1756" height="704" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.1.png" alt="Business Dashboard Admin part - General settings" class="wp-image-45314" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.1.png 1756w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.1-300x120.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.1-1024x411.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.1-768x308.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.1-1536x616.png 1536w" sizes="auto, (max-width: 1756px) 100vw, 1756px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h3 id="h-1-2-the-configuration-tab-new-dashboard-defaults-settings" class="wp-block-heading">1.2. The Configuration tab &#8211; &#8220;New Dashboard Defaults&#8221; settings</h3>



<p class="wp-block-paragraph">The second section inside the configuration tab allows to overwrite the default settings for <strong><em>new</em></strong> dashboards. As I mentioned in previous blogs, a dashboard has around 10 configurable parameters, that controls things like which features are allowed, whether users can refresh or if it should be automatic, etc. All these parameters have a default value, which is usually &#8220;No&#8221; (disabled / do not bypass important checks).</p>



<p class="wp-block-paragraph">This section will make it so that all dashboards created in the future will use your own personalized default values, so you don&#8217;t have to configure the same things over and over again. It doesn&#8217;t have any impact on already configured dashboards AND it doesn&#8217;t force anything. It is simply a replacement of default values, which means that you can still overwrite these settings inside each dashboards as required (because specific dashboards might have different needs).</p>



<ul class="wp-block-list">
<li><strong><em>Auto-refresh enabled</em></strong>: controls whether the automatic refresh is enabled. (default: No).</li>



<li><strong><em>Auto-refresh interval (seconds)</em></strong>: controls the interval between two automatic refresh. (default: 300).</li>



<li><strong><em>User can toggle auto-refresh</em></strong>: controls whether users can enable the automatic refresh. (default: No).</li>



<li><strong><em>Export to PDF enabled</em></strong>: controls whether the PDF export is enabled. (default: No).</li>



<li><strong><em>Export to CSV enabled</em></strong>: controls whether the CSV export is enabled. (default: No).</li>



<li><strong><em>Drill-through enabled</em></strong>: controls whether the drill-through is enabled, which also controls the navigation to target objects. (default: No).</li>



<li><strong><em>Drill-through max results</em></strong>: controls how many results will be paginated on drill-through modals. (default: 300).</li>



<li><strong><em>Server scan max results</em></strong>: controls how many results we can return when looking for objects. (default: 500).</li>



<li><strong><em>Skip template check</em></strong>: controls whether the &#8220;template check&#8221; are skipped. More on that later in this post. (default: No).</li>



<li><strong><em>Skip object permission check</em></strong>: controls whether the user&#8217;s ACLs are respected. More on that later in this post. (default: No).</li>
</ul>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab12138890&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab12138890" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1756" height="970" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.2.png" alt="Business Dashboard Admin part - Default settings" class="wp-image-45315" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.2.png 1756w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.2-300x166.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.2-1024x566.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.2-768x424.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.2-1536x848.png 1536w" sizes="auto, (max-width: 1756px) 100vw, 1756px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h3 id="h-1-3-the-configuration-tab-logging-settings" class="wp-block-heading">1.3. The Configuration tab &#8211; &#8220;Logging&#8221; settings</h3>



<p class="wp-block-paragraph">The third section contains all the standard M-Files logging configurations. In there, you can enable the global logging, then create some specific targets, like a file target (writing to a log file on the local file system), event log target (writing to the Windows Event Log), database target (writing to a specific DB), etc&#8230;</p>



<p class="wp-block-paragraph">These are usual M-Files configurations, so I won&#8217;t describe them all, but in short, you only have to enable the global logging, defining a target with the appropriate log level and target-specific details as you see fit.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab12138ea4&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab12138ea4" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1754" height="1038" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.3.png" alt="Business Dashboard Admin part - Logging settings" class="wp-image-45316" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.3.png 1754w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.3-300x178.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.3-1024x606.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.3-768x454.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.1.3-1536x909.png 1536w" sizes="auto, (max-width: 1754px) 100vw, 1754px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-2-the-dashboard-list-and-action-buttons" class="wp-block-heading">2. The dashboard list and action buttons</h2>



<p class="wp-block-paragraph">The Dashboard tab shows all currently configured dashboards (or nothing if you have none yet) in an alternating-row list. Each row contains additional details such as the count of widget inside this dashboard, the access details (whether it can be accessed by everybody or if it&#8217;s restricted to some people/groups only) and the description.</p>



<p class="wp-block-paragraph">Each dashboard row also has a compact <strong>icon button bar</strong> for quick access: <code>↑ ↓ 🖉 ⧉ ⎙ 🗑</code> (move up / move down / edit / clone / export / delete) (It took me so long to find these icons!). These per-row buttons provide faster access to the most common operations without opening a selector modal from the top-bar buttons.</p>



<p class="wp-block-paragraph">Speaking of top-bar buttons, you saw them already, but at the very top, you can see global action buttons available from any of the tabs:</p>



<figure class="wp-block-table"><table><thead><tr><th>Button</th><th>Action</th></tr></thead><tbody><tr><td><strong>Add New</strong></td><td>Opens the editor pre-filled with a blank dashboard template (Visual Designer mode by default)</td></tr><tr><td><strong>Import</strong></td><td>Imports one or more dashboards from a <strong><em>.json</em></strong> file (merges with existing)</td></tr><tr><td><strong>Reorder&#8230;</strong></td><td>Opens a modal page, with the list of all dashboards to re-order them</td></tr><tr><td><strong>Edit&#8230;</strong></td><td>Opens a selector to choose and edit an existing dashboard</td></tr><tr><td><strong>Clone&#8230;</strong></td><td>Duplicates an existing dashboard with a new UUID and a <strong><em>&#8221; (copy)&#8221;</em></strong> name suffix</td></tr><tr><td><strong>Export&#8230;</strong></td><td>Opens a selector to choose and export one or all dashboards, to a <strong><em>.json</em></strong> file</td></tr><tr><td><strong>Delete&#8230;</strong></td><td>Opens a selector to choose and delete one or all dashboards, with a confirmation message</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">More on these actions lower in this blog post.</p>



<h2 id="h-3-the-editor-two-modes-one-source-of-truth" class="wp-block-heading">3. The editor: two modes, one source of truth</h2>



<p class="wp-block-paragraph">When you click <strong><em>Add New</em></strong> or <strong><em>Edit&#8230;</em></strong>, the editor modal opens. At the top of the toolbar, two buttons toggle between the modes:</p>



<ul class="wp-block-list">
<li><strong>[Visual]</strong> <em>(default)</em>: the &#8220;Visual Designer&#8221;, a form-based designer with a canvas of widget tiles and a side configuration panel.</li>



<li><strong>[JSON]</strong>: the &#8220;JSON Editor&#8221;, a manual code box that allows you to write JSON.</li>
</ul>



<p class="wp-block-paragraph">The two modes are <strong>kept in sync at all times</strong>. Switching from Visual to JSON shows the current visual state serialized, switching back re-parses the JSON so that the Visual Designer is also up-to-date.</p>



<p class="wp-block-paragraph">The editor is split into four regions:</p>



<ul class="wp-block-list">
<li><strong>The top bar</strong> shows main buttons to switch between modes, load an example dashboard (to give you some ideas), format JSON (when in JSON Editor mode), test all widgets at once or save.</li>



<li><strong>The</strong> <strong>left panel</strong> shows either the widgets display in Visual Designer mode or the JSON code block in JSON Editor mode.</li>



<li><strong>The footer bar</strong> (anchored below the canvas) has four buttons, to show the dashboard settings, add a new widget, clone an existing widget or test a widget. There is also a small place below these buttons kept for status messages, to let you know when something was done or if there are errors, etc.</li>



<li><strong>The config panel (right panel)</strong> shows the editable fields. In Visual Designer mode, it shows dashboards and widget configurations. In JSON Editor mode, it shows the user and group pickers only. Both modes also includes the &#8220;Test Results&#8221; which contains execution/validation details.</li>
</ul>



<h3 id="h-3-1-visual-mode" class="wp-block-heading">3.1. Visual mode</h3>



<p class="wp-block-paragraph">As said, the left panel contains the widget display, with their title, type, aggregation, objectType. It also display quick action buttons such as up-down or delete, so you can quickly re-arrange the widgets and get the visual look&amp;feel that you want. The right panel contains the dashboard settings by default (which we described above already). When a widget is selected, the right panel will then display these different building blocks:</p>



<figure class="wp-block-table"><table><thead><tr><th>Section</th><th>Fields</th></tr></thead><tbody><tr><td><strong>Basics</strong></td><td>Widget title, description, translations, type and size (Col Span (1-12), Row Span (1-12))</td></tr><tr><td><strong>Data Source</strong></td><td>Object Type(s), Class and aggregation type</td></tr><tr><td><strong>Filters</strong></td><td>Add / remove filter rows, with their associated properties, operators, value type and value</td></tr><tr><td><strong>Aggregation</strong></td><td>The details of the aggregation, like grouping property, bucket size, reducer, reducer property, series property</td></tr><tr><td><strong>Display</strong></td><td>Any possible display options such as thresholds (coloring), unit, min / max, decimals (0-9), smooth or bar layout</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">With this Visual Designer, you are guided by selecting things one by one. You don&#8217;t see ALL options right from the beginning, depending on what you select, only what is possible will be offered, so you can focus on what makes sense for you.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab121398ba&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab121398ba" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="2412" height="1670" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.1.png" alt="Business Dashboard Visual Designer" class="wp-image-45317" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.1.png 2412w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.1-300x208.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.1-1024x709.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.1-768x532.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.1-1536x1063.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.1-2048x1418.png 2048w" sizes="auto, (max-width: 2412px) 100vw, 2412px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h3 id="h-3-2-json-mode" class="wp-block-heading">3.2. JSON mode</h3>



<p class="wp-block-paragraph">The JSON Editor is simply a code box which allows you to enter JSON. It shares the top-bar buttons with the Visual Designer, the only difference being the &#8220;Format JSON&#8221; one that is enabled only when in JSON mode.</p>



<p class="wp-block-paragraph">I might extend this feature later, but for now there is no completion feature, it simply has a bit of highlighting so you can spot errors easily and allows for code drag-and-drop.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab12139ed9&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab12139ed9" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="2410" height="1670" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.2.png" alt="Business Dashboard JSON Editor" class="wp-image-45318" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.2.png 2410w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.2-300x208.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.2-1024x710.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.2-768x532.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.2-1536x1064.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.3.2-2048x1419.png 2048w" sizes="auto, (max-width: 2410px) 100vw, 2410px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-4-validation" class="wp-block-heading">4. Validation</h2>



<h3 id="h-4-1-the-two-stage-validators" class="wp-block-heading">4.1. The two-stage validators</h3>



<p class="wp-block-paragraph">Whether you click on <strong><em>Test Queries</em></strong> (all widgets), <strong><em>Test Widget</em></strong> (1 widget) or <strong><em>Save</em></strong> and whether you are doing it from Visual or JSON mode, the engine will run a 2-stage validation of what you currently configured.</p>



<h4 id="h-4-1-1-stage-1" class="wp-block-heading">4.1.1. Stage 1</h4>



<p class="wp-block-paragraph">The first stage is simply a structural one. For example, are all mandatory fields present, are known values (e.g. widget types) within the accepted ones, etc. This is very fast and it doesn&#8217;t require a connection to the vault, so that we can already surface global issues.</p>



<p class="wp-block-paragraph"><strong>Note:</strong> Only the &#8220;Import&#8221; feature bypasses the stage-2 validation and only performs a structural check. When using the import feature, we kinda expect that the JSON was exported from another vault and therefore if it existed inside a previous environment, it means that it is a valid dashboard. Doing the stage-2 validation on each import would considerably slow it down (especially if you import 10, 50 or even 100 dashboards at once).</p>



<h4 id="h-4-1-2-stage-2" class="wp-block-heading">4.1.2. Stage 2</h4>



<p class="wp-block-paragraph">The second stage is much more complex and it will verify if you made any mistakes by really checking what&#8217;s available inside the vault. This will mark typos such as wrong object types, classes or properties. It will also try to resolve value lists items that you references, to make sure they are indeed present. This is obviously slightly slower, but still completes within a couple seconds for most dashboards. It also gives you a good idea of how fast this dashboard will load for end-users, since it&#8217;s actually executing all queries.</p>



<p class="wp-block-paragraph"><strong>Note:</strong> For Test Widget, the engine will only test one specific widget, as said earlier. Therefore, it automatically adds a valid dashboard structure around it so it can pass the stage-1 validation for the dashboard level. Only stage-1 validation of the widget level is tested and the query execution for the phase-2, obviously.</p>



<h3 id="h-4-2-errors-and-warnings" class="wp-block-heading">4.2. Errors and warnings</h3>



<p class="wp-block-paragraph">The Test Results panel shows three possible statuses per widget:</p>



<ul class="wp-block-list">
<li><strong>OK</strong>: the query succeeded and the result is renderable.</li>



<li><strong>Warning</strong> (amber): the query succeeded but the result will probably not be rendered the way you would like to. A common occurrence is a date-valued reducer on a chart widget, where the message will tell you to switch to <strong><em>kpiNumber</em></strong>, <strong><em>table</em></strong>, or <strong><em>gauge</em></strong>.</li>



<li><strong>Error</strong> (red): the query failed. The message tells you which property name or class name to check or change.</li>
</ul>



<p class="wp-block-paragraph">Note: A warning will still allow the Save to complete, because even if it might not render, a placeholder widget will replace the expected chart. On the other hand, an error will prevent the Save operation because we shouldn&#8217;t persist something that is structurally or fundamentally wrong. Here is an example from the Example Dashboard where I specifically introduced some mistakes:</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1213a7a3&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1213a7a3" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="2412" height="1670" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.4.2.png" alt="Business Dashboard JSON Editor with errors, warnings and OK messages" class="wp-image-45319" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.4.2.png 2412w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.4.2-300x208.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.4.2-1024x709.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.4.2-768x532.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.4.2-1536x1063.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.4.2-2048x1418.png 2048w" sizes="auto, (max-width: 2412px) 100vw, 2412px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-5-import-and-export" class="wp-block-heading">5. Import and Export</h2>



<p class="wp-block-paragraph">Dashboards live in the vault&#8217;s Named Value Storage (NVS). To facilitate transfer from DEV to QA/INT to PROD or any other environments you might have, an import/export feature was added on the M-Files Admin. To use it, simply click on the <strong><em>Import</em></strong> or <strong><em>Export&#8230;</em></strong> buttons.</p>



<p class="wp-block-paragraph">First, the <strong><em>Export&#8230;</em></strong> one, open a prompt so you can select which dashboard you would like to export. On that prompt, there is also a button to <strong><em>Export All</em></strong>. If you select a single dashboard to be exported, the JSON file generated will have, by default, the name of the dashboard (you can set the name that you want, of course). If you chose to export all, then the name will simply be &#8220;dashboards-export.json&#8221; and inside, you will find an array of dashboard definition.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1213adcc&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1213adcc" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1257" height="869" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.5.1.png" alt="Business Dashboard Export Dashboard" class="wp-image-45320" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.5.1.png 1257w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.5.1-300x207.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.5.1-1024x708.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.5.1-768x531.png 768w" sizes="auto, (max-width: 1257px) 100vw, 1257px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<p class="wp-block-paragraph">Once it has been exported, you can then <strong><em>Import</em></strong> it somewhere else (or on the same vault, it will merge it with existing dashboards &#8211; that&#8217;s why IDs are important!). Another prompt opens and you can then select a file (through Windows Explorer) or you can also drag-and-drop JSON to it. Its content is then displayed inside the prompt so you can take a look. Once you hit the import button at the bottom, it will quickly validate the dashboard definition and if everything is good, it will be imported into the vault.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1213b328&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1213b328" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1263" height="929" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.5.2.png" alt="Business Dashboard Import Dashboard" class="wp-image-45321" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.5.2.png 1263w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.5.2-300x221.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.5.2-1024x753.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/7.5.2-768x565.png 768w" sizes="auto, (max-width: 1263px) 100vw, 1263px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-6-the-reorder-and-clone-modals" class="wp-block-heading">6. The Reorder and Clone modals</h2>



<p class="wp-block-paragraph">Two smaller actions but these are still worth mentioning because they fix small but real pain points. The <strong><em>Reorder&#8230;</em></strong> one allows you to use the up-down arrows &#8211; same as from the dashboard list shown in the default Dashboard tab. But you have access to another facilitator with the global button: dashboards are numbered with a small text box that you can update. For example, if you have 5 dashboards, the last one will have a small text box with a &#8220;5&#8221; inside. If you update that value to &#8220;1&#8221;, then this dashboard will automatically become the 1st one. This allows for faster re-ordering than clicking 4 times on the arrow-up icon, especially when you have 50+ dashboards to manage.</p>



<p class="wp-block-paragraph">If you have a good idea of what you want to build and you know that one of your dashboards is already pretty similar, then instead of recreating everything from scratch, you can use the <strong><em>Clone&#8230;</em></strong> feature. It allows the duplication of an existing dashboard into a new one. The new one will use new IDs for both the dashboard and all the widgets, and to be able to differentiate them, the name will have a <strong><em>&#8221; (copy)&#8221;</em></strong> suffix. I see you coming &#8211; if you duplicate the same dashboard multiple times, it will be <strong><em>&#8221; (copy 2)&#8221;</em></strong>, then <strong><em>&#8221; (copy 3)&#8221;</em></strong>, etc.</p>



<h2 id="h-7-access-control-two-level-model" class="wp-block-heading">7. Access control &#8211; two-level model</h2>



<p class="wp-block-paragraph">Access control in the Business Dashboard works on two distinct levels:</p>



<figure class="wp-block-table"><table><thead><tr><th>Level</th><th>Field</th><th>Default</th><th>What it controls</th></tr></thead><tbody><tr><td>Dashboard visibility</td><td><strong><em>assignedTo</em></strong></td><td>Public</td><td>Who can see and open the dashboard</td></tr><tr><td>Per-object data filtering</td><td><strong><em>skipObjectPermissionCheck</em></strong></td><td><strong><em>&#8220;No&#8221;</em></strong> (= check = ACLs are respected)</td><td>Whether widget data respects each user&#8217;s individual M-Files object ACLs / permissions</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">The two are independent. <strong><em>assignedTo</em></strong> says &#8220;you are allowed to open this dashboard&#8221;. <strong><em>skipObjectPermissionCheck</em></strong> says &#8220;once you are inside, do widgets show you everything or only what your own permissions allows you to see and read&#8221;.</p>



<h3 id="h-7-1-assignedto-who-can-open-the-dashboard" class="wp-block-heading">7.1. assignedTo &#8211; who can open the dashboard</h3>



<p class="wp-block-paragraph">By default (no <strong><em>assignedTo</em></strong> field or an empty array), a dashboard is visible to <strong>everyone</strong> who has access to the vault. To restrict the access, you can define either a list of users or groups:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;assignedTo&quot;: {
  &quot;users&quot;: &#x5B;&quot;Morgan.Patou&quot;],
  &quot;groups&quot;: &#x5B;&quot;Finance Team&quot;, &quot;Management&quot;]
}
</pre></div>


<p class="wp-block-paragraph">Access is granted to <strong>any user who matches any entry</strong> (OR semantics), i.e. part of the users list or part of any of the group from the groups list.</p>



<h3 id="h-7-2-skipobjectpermissioncheck-what-data-shows-up" class="wp-block-heading">7.2. skipObjectPermissionCheck &#8211; what data shows up</h3>



<p class="wp-block-paragraph">By default (<strong><em>&#8220;No&#8221;</em></strong>), M-Files permissions will be respected, so you cannot see things that you aren&#8217;t entitled to see. However, there are valid use cases where you would need to specifically bypass object-level permissions.</p>



<p class="wp-block-paragraph">For example, you probably don&#8217;t want all your employees to see all the HR documents, all the contracts, etc. However, there are often KPIs that could be rendered &#8220;Public&#8221;, or at least given to a broader audience than the people that originally have access to these objects. Maybe you would like to share some executive or management reporting KPIs, some Sales Performance details, or some Security Information.</p>



<p class="wp-block-paragraph">That is entirely up to you and, in addition, you control whether end-users can use the drill-through or not and what metadata the drill-through should show (if anything at all). Again, it&#8217;s not because you show KPIs that users will be able to find and read classified documents. M-Files permissions on the objects themselves (preview its content, listing all metadata, downloading the file, etc.) will always be respected. What the Business Dashboard allows is fully configurable, so there are no real security concerns.</p>



<p class="wp-block-paragraph"><strong>When to use each:</strong></p>



<ul class="wp-block-list">
<li><strong>&#8220;No&#8221;</strong> (default): when different users should see different numbers based on their own M-Files ACL.</li>



<li><strong>&#8220;Yes&#8221;</strong>: when every authorized viewer should see identical numbers and performance matters (typical for overview dashboards that don&#8217;t go into details). Combine with <strong><em>assignedTo</em></strong> to restrict who can open it.</li>
</ul>



<h2 id="h-8-performance" class="wp-block-heading">8. Performance</h2>



<p class="wp-block-paragraph">I already mentioned them in earlier posts, but I think it still makes sense to summarize it all again here, since we are talking about the Admin side of the <strong>Business Dashboard</strong>. There are multiple parameters that have performance impacts. Some of them are very low, some aren&#8217;t.</p>



<h3 id="h-8-1-performance-gains" class="wp-block-heading">8.1 Performance gains</h3>



<p class="wp-block-paragraph">These are the things that will have either no negative performance impact or even show performance gains:</p>



<ul class="wp-block-list">
<li><strong><em>exportToPdfEnabled</em></strong> / <strong><em>exportToCsvEnabled</em></strong>: these two parameters are similar in the sense that they only enable one specific feature and it has no performance impact, since it runs fully on client side. All the data is already available in the client&#8217;s dashboard, so it has no impact on the server.</li>



<li><strong><em>skipObjectPermissionCheck</em></strong>: by default, it will execute ACLs checks to ensure users only see objects they are entitled to. The way I implemented that means that whether you are doing server-level queries or user-level queries has absolutely no impact on the performance. In fact, it might be even faster to execute the ACLs checks, since you might return less objects that what the server would have access to. Therefore, the performance impact is null or slightly positive.</li>



<li><strong><em>objectType</em></strong> / <strong><em>class</em></strong> / <strong><em>count reducer</em></strong> / <strong><em>native filters</em></strong>: adding a restriction on class and adding native filters will mean less results to be fetched and processed. As previously said (c.f. <a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/" target="_blank" rel="noreferrer noopener">Post 5</a>), for optimal performance, make sure to restrict the objectTypes / class and apply native filters as much as possible. Therefore, adding these will definitively bring performance gains.</li>
</ul>



<h3 id="h-8-2-potential-for-medium-high-performance-impact" class="wp-block-heading">8.2. Potential for Medium / High performance impact</h3>



<p class="wp-block-paragraph">Depending on how you use them, these settings can have very low or very high impact on the performance of the <strong>Business Dashboard</strong>:</p>



<ul class="wp-block-list">
<li><strong><em>autoRefreshEnabled</em></strong> / <strong><em>autoRefreshIntervalSeconds</em></strong> / <strong><em>userCanToggleAutoRefresh</em></strong>: control the refresh of widgets/dashboards. The impact depends on multiple factors, like how many users, widgets, objects fetched, etc&#8230; These will increase the &#8220;regular&#8221; load on the server, not really impact the loading speed&#8230; The performance impact can go from very low (e.g. refresh every 5 minutes with low-demanding widgets) to pretty high (e.g. refresh every 15 seconds with 10&#8217;000+ objects widgets)&#8230;</li>



<li><strong><em>serverScanMaxResults</em></strong> / <strong><em>drillThroughEnabled</em></strong> / <strong><em>drillThroughMaxResults</em></strong>: define the maximum results for the widget or the drill-through. When hit, a <strong><em>Partial results</em></strong> badge appears, to show results may be incomplete. These are basically <strong>fail-safe/lifeline</strong>, to make sure widgets do not try to retrieve the whole vault&#8217;s content. The performance impact will be important if you disable this (&#8220;0&#8221; = unlimited results) and if you try to fetch 100&#8217;000 objects.</li>



<li><strong><em>skipTemplateCheck</em></strong> / <strong><em>non-count reducers</em></strong> / <strong><em>post-filters</em></strong>: all these parameters means that there is a need to fetch additional metadata from the vault, for all objects. If you want to execute the template check, apply a non-count reducer on a property or a post-filter, you will first fetch all objects, and then, for each of them, fetch their associated metadata. As far as I culd see, M-Files doesn&#8217;t allow to return both objects and their metadata at the same time. The performance impact will be grow with the number of objects you fetch and need to process.</li>
</ul>



<p class="wp-block-paragraph"><strong>Note:</strong> The performance isn&#8217;t bad. There are a few tricks I implemented to make sure the performance stays pretty good, so you can load a dashboard fetching 5&#8217;000+ objects in less than 1.5 seconds, with the right configuration.</p>



<h2 id="h-11-wrap-up" class="wp-block-heading">11. Wrap-up</h2>



<p class="wp-block-paragraph">The Admin tab is the place administrators will spend the majority of their time. The Visual Designer is there to lower the complexity of creating and managing dashboards. Even a JSON-averse administrator can build a full dashboard without ever opening the JSON editor. However, it is still available for power users that feel comfortable with it, as it gives them the flexibility, quick copy/paste, and bulk-edit comfort they can expect.</p>



<p class="wp-block-paragraph">The next, and probably last, <strong><a href="https://www.dbi-services.com/blog/m-files-bd-putting-it-together-with-the-visual-designer/" id="45433" target="_blank" rel="noreferrer noopener">Post 8</a></strong> will walk you through building a complete Contracts dashboard on the M-Files Sample Vault. I will use the Visual Designer only, so you can follow along and adapt it to your own vault.</p>



<p class="wp-block-paragraph">Want to know more about this Business Dashboard? <a href="https://www.dbi-services.com/company/contact/" target="_blank" rel="noreferrer noopener">Contact us</a> and we will be happy to showcase it on <a href="https://www.m-files.com/" target="_blank" rel="noreferrer noopener">M-Files</a>.</p>



<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-the-admin-part/">M-Files BD &#8211; The Admin part</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/m-files-bd-the-admin-part/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>M-Files BD &#8211; Aggregations, reducers and series</title>
		<link>https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/</link>
					<comments>https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/#respond</comments>
		
		<dc:creator><![CDATA[Morgan Patou]]></dc:creator>
		<pubDate>Tue, 23 Jun 2026 22:38:36 +0000</pubDate>
				<category><![CDATA[Enterprise content management]]></category>
		<category><![CDATA[Aggregation]]></category>
		<category><![CDATA[Business Dashboard]]></category>
		<category><![CDATA[M-Files]]></category>
		<category><![CDATA[Reducer]]></category>
		<category><![CDATA[Series]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=45206</guid>

					<description><![CDATA[<p>In the previous post, I walked through the query side of the dashboard JSON: objectType, class, filters, date tokens. What I deliberately left out was the aggregation block, because it deserves its own post. The aggregation is what transforms the set of matching objects into a value the widget can render. There are five aggregation [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/">M-Files BD &#8211; Aggregations, reducers and series</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In the <a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/" target="_blank" rel="noreferrer noopener">previous post</a>, I walked through the query side of the dashboard JSON: <strong><em>objectType</em></strong>, <strong><em>class</em></strong>, <strong><em>filters</em></strong>, <strong><em>date tokens</em></strong>. What I deliberately left out was the <strong><em>aggregation</em></strong> block, because it deserves its own post.</p>



<p class="wp-block-paragraph">The aggregation is what transforms the set of matching objects into a value the widget can render. There are five aggregation types, six reducers, and one optional second dimension (<strong><em>seriesProperty</em></strong>). Together, they cover every widget shape from a single KPI number to a cross-tab pivot table.</p>



<h2 id="h-1-the-two-dimensional-model" class="wp-block-heading">1. The two-dimensional model</h2>



<p class="wp-block-paragraph">The aggregation has two complementary components:</p>



<ul class="wp-block-list">
<li>The aggregation <strong><em>type</em></strong> is the <strong>shape</strong> of the result: one value (<strong><em>summary</em></strong>), one value per group (<strong><em>groupByProperty</em></strong>), one value per time bucket (<strong><em>groupByDateBucket</em></strong>), one value per admin-defined range (<strong><em>groupByRange</em></strong>), or a simple list of objects (<strong><em>list</em></strong>).</li>



<li>The aggregation <strong><em>reducer</em></strong> is the <strong>operation</strong> applied within each group (or over the whole matching set for <strong><em>summary</em></strong>): <strong><em>count</em></strong> (default), <strong><em>sum</em></strong>, <strong><em>avg</em></strong>, <strong><em>min</em></strong>, <strong><em>max</em></strong>, <strong><em>median</em></strong>. Note: The reducer is ignored for <strong><em>list</em></strong>.</li>
</ul>



<p class="wp-block-paragraph">In other words, <strong><em>type</em></strong> decides whether you get a number, a chart, or a table; <strong><em>reducer</em></strong> decides what that number, bar, or cell actually measures.</p>



<h2 id="h-2-the-five-aggregation-types" class="wp-block-heading">2. The five aggregation types</h2>



<h3 id="h-2-1-summary-one-value-over-everything" class="wp-block-heading">2.1. summary &#8211; one value over everything</h3>



<p class="wp-block-paragraph"><strong><em>summary</em></strong> reduces the entire matching set into a single value. By default it counts the matching objects. However, with a different reducer, you can make it compute a property aggregate in different ways. Here are a few example of summary aggregations:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
// Total count of matching objects
&quot;aggregation&quot;: { &quot;type&quot;: &quot;summary&quot; }

// Total revenue of matching objects
// (sum all values from the &quot;Amount&quot; property)
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;summary&quot;,
  &quot;reducer&quot;: &quot;sum&quot;,
  &quot;reducerProperty&quot;: &quot;Amount&quot;
}

// Latest contract expiry date
// (display only the date with the highest value - drill-through still list all objects)
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;summary&quot;,
  &quot;reducer&quot;: &quot;max&quot;,
  &quot;reducerProperty&quot;: &quot;Effective through&quot;
}
</pre></div>


<p class="wp-block-paragraph">The widgets that accept <strong><em>summary</em></strong> are <strong><em>kpiNumber</em></strong>, <strong><em>gauge</em></strong>, and <strong><em>table</em></strong>. If you followed this series, you probably saw a bunch of the first two, already. The last one, in this case, will render the value as a one-row table. For all other chart widgets (donut, bar, line, area), <strong><em>summary</em></strong> does not really make visual sense, as a single value cannot be plotted on an X-Y axis.</p>



<h3 id="h-2-2-groupbyproperty-one-value-per-group" class="wp-block-heading">2.2. groupByProperty &#8211; one value per group</h3>



<p class="wp-block-paragraph"><strong><em>groupByProperty</em></strong> groups objects by the distinct values of a property (the <strong><em>propertyName</em></strong>) and applies the reducer to each group independently. Here are two examples:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
// Count of matching objects per Agreement type
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByProperty&quot;,
  &quot;propertyName&quot;: &quot;Agreement type&quot;,
  &quot;includeEmptyResults&quot;: &quot;No&quot;
}

// Total revenue of matching objects per Customer
// (sum all values from the &quot;Amount&quot; property for each Customer independently)
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByProperty&quot;,
  &quot;propertyName&quot;: &quot;Customer&quot;,
  &quot;reducer&quot;: &quot;sum&quot;,
  &quot;reducerProperty&quot;: &quot;Amount&quot;
}
</pre></div>


<p class="wp-block-paragraph">The supported fields are the following ones:</p>



<figure class="wp-block-table"><table><thead><tr><th>Field</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><em>propertyName</em></strong></td><td>Yes</td><td>Property to group by (any type is supported)</td></tr><tr><td><strong><em>reducer</em></strong></td><td>No</td><td>Defaults to <strong><em>count</em></strong> (c.f. section 1 above)</td></tr><tr><td><strong><em>reducerProperty</em></strong></td><td>Yes, when reducer != count</td><td>Property to reduce within each group. Can be the same as <strong><em>propertyName</em></strong> but it can also be different (c.f. above)</td></tr><tr><td><strong><em>includeEmptyResults</em></strong></td><td>No</td><td>When set to <strong><em>&#8220;Yes&#8221;</em></strong>, it adds a <strong><em>(none)</em></strong> group which will contain objects without value (e.g. no value for &#8220;Customer&#8221; property)</td></tr><tr><td><strong><em>seriesProperty</em></strong></td><td>No</td><td>Splits the chart into multiple series (c.f. section 4 below)</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">The widgets that accept <strong><em>groupByProperty</em></strong> are <strong><em>donut</em></strong>, <strong><em>bar</em></strong>, <strong><em>line</em></strong>, <strong><em>area</em></strong> and <strong><em>table</em></strong>. Basically, all charts widgets, plus the table which is kind of a Swiss knife, that works with everything.</p>



<h3 id="h-2-3-groupbydatebucket-one-value-per-time-period" class="wp-block-heading">2.3. groupByDateBucket &#8211; one value per time period</h3>



<p class="wp-block-paragraph">It is very similar to the <strong><em>groupByProperty</em></strong>, but the target must be a date / timestamp. Basically, something that contains a date, whether its only the date or a full date-time is fine. Because dates would probably be a bit too wide, there is a concept of buckets, to group dates by a pre-defined range that might make sense. The default time periods are <strong><em>day</em></strong>, <strong><em>week</em></strong>, <strong><em>month</em></strong>, <strong><em>quarter</em></strong> or <strong><em>year</em></strong>. Here are two examples:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
// Count of contracts expiring per month
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByDateBucket&quot;,
  &quot;propertyName&quot;: &quot;Effective through&quot;,
  &quot;bucketSize&quot;: &quot;month&quot;,
  &quot;includeEmptyResults&quot;: &quot;Yes&quot;
}

// Sum of invoice amounts per quarter
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByDateBucket&quot;,
  &quot;propertyName&quot;: &quot;Invoice date&quot;,
  &quot;bucketSize&quot;: &quot;quarter&quot;,
  &quot;reducer&quot;: &quot;sum&quot;,
  &quot;reducerProperty&quot;: &quot;Amount&quot;
}
</pre></div>


<p class="wp-block-paragraph">The supported fields are pretty similar to the <strong><em>groupByProperty</em></strong>:</p>



<figure class="wp-block-table"><table><thead><tr><th>Field</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><em>propertyName</em></strong></td><td>Yes</td><td>Property to group by (date / timestamp only)</td></tr><tr><td><strong><em>bucketSize</em></strong></td><td>No</td><td>Defaults to <strong><em>month</em></strong>, the size of the range to group by</td></tr><tr><td><strong><em>reducer</em></strong></td><td>No</td><td>Same as <strong><em>groupByProperty</em></strong></td></tr><tr><td><strong><em>reducerProperty</em></strong></td><td>Yes, when reducer != count</td><td>Same as <strong><em>groupByProperty</em></strong></td></tr><tr><td><strong><em>includeEmptyResults</em></strong></td><td>No</td><td>Same as <strong><em>groupByProperty</em></strong>, but <strong><em>&#8220;Yes&#8221;</em></strong> will also fill gaps with zero values (e.g. a month without revenue is still displayed as <strong><em>&#8220;0&#8221;</em></strong>, it&#8217;s not silently ignored)</td></tr><tr><td><strong><em>seriesProperty</em></strong></td><td>No</td><td>Same as <strong><em>groupByProperty</em></strong></td></tr></tbody></table></figure>



<p class="wp-block-paragraph"><strong><em>Note:</em></strong> you would usually combine <strong><em>groupByDateBucket</em></strong> with a date filter to limit the range of results. Without filters, the chart would show everything from the earliest object in the vault to the most recent, possibly spanning decades. If that&#8217;s what you want to see, then that&#8217;s absolutely fine. However, most of the time, a filter like <strong><em>between @startOfYear and @endOfYear</em></strong> for an annual trend chart might be more appropriate.</p>



<h3 id="h-2-4-groupbyrange-one-value-per-admin-defined-range" class="wp-block-heading">2.4. groupByRange &#8211; one value per admin-defined range</h3>



<p class="wp-block-paragraph">This one is also very similar to the last two groupings. With the main difference that this is the only one that allows you to define the exact range.</p>



<h4 id="h-2-4-1-the-groupbyrange-itself" class="wp-block-heading">2.4.1 The groupByRange itself</h4>



<p class="wp-block-paragraph">If you have a certain property that has a high cardinality, it might be difficult to display it with a <strong><em>groupByProperty</em></strong>. Let&#8217;s take for example the &#8220;Amount&#8221; property from before. When you want to apply math on it (sum/avg/min/max/median), then that&#8217;s fine because it only returns one value. But if you want to see the revenue themselves, without a prior grouping on something else (e.g. above we first group by &#8220;Customer&#8221;), then you would end-up with dozens/hundreds/thousands of groups? That&#8217;s where <strong><em>groupByRange</em></strong> shines, because you define the grouping (e.g. 0-1&#8217;000, 1&#8217;000-5&#8217;000, &gt;=5&#8217;000).</p>



<p class="wp-block-paragraph">You can use that grouping method with numeric / time / text / lookup properties. On the other hand, date / timestamp and boolean aren&#8217;t supported. The reason for that is simple: there is already <strong><em>groupByDateBucket</em></strong> for date / timestamp, and boolean can only have 2 values (Yes / No), so <strong><em>groupByProperty</em></strong> works just fine.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
// Invoices by specific range
// 3 ranges: 0-1000, 1000-5000, 5000-10000
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByRange&quot;,
  &quot;propertyName&quot;: &quot;Amount&quot;,
  &quot;boundaries&quot;: &#x5B;&quot;0&quot;, &quot;1000&quot;, &quot;5000&quot;, &quot;10000&quot;]
}

// Invoices by specific range, with open ranges
// 4 ranges: 0-1000, 1000-5000, 5000-10000, &gt;=10000
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByRange&quot;,
  &quot;propertyName&quot;: &quot;Amount&quot;,
  &quot;boundaries&quot;: &#x5B;&quot;*&quot;, &quot;1000&quot;, &quot;5000&quot;, &quot;10000&quot;, &quot;*&quot;]
}

// Efficiency / speed of processing / duration of some actions / etc...
// 4 ranges: 0-30s, 30s-1min, 1min-2min, &gt;=2min
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByRange&quot;,
  &quot;propertyName&quot;: &quot;Duration&quot;,
  &quot;boundaries&quot;: &#x5B;&quot;*&quot;, &quot;00:00:30&quot;, &quot;00:01:00&quot;, &quot;00:02:00&quot;, &quot;*&quot;]
}

// Customers by country name
// 3 ranges: A-F, F-M, &gt;=M
// e.g. &quot;France&quot; in 2nd group, Switzerland in 3rd group
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByRange&quot;,
  &quot;propertyName&quot;: &quot;Country&quot;,
  &quot;boundaries&quot;: &#x5B;&quot;A&quot;, &quot;F&quot;, &quot;M&quot;, &quot;*&quot;],
  &quot;reducer&quot;: &quot;count&quot;
}
</pre></div>


<p class="wp-block-paragraph">Again, the supported fields are fairly similar to the <strong><em>groupByProperty</em></strong>:</p>



<figure class="wp-block-table"><table><thead><tr><th>Field</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><em>propertyName</em></strong></td><td>Yes</td><td>Property to group by (numeric / time / text / lookup only)</td></tr><tr><td><strong><em>boundaries</em></strong></td><td>Yes</td><td><strong>Ordered</strong> array of range boundary values (at least 2 values, sorted ascending, with at least 1 non &#8220;*&#8221; value)</td></tr><tr><td><strong><em>reducer</em></strong></td><td>No</td><td>Same as <strong><em>groupByProperty</em></strong></td></tr><tr><td><strong><em>reducerProperty</em></strong></td><td>Yes, when reducer != count</td><td>Same as <strong><em>groupByProperty</em></strong></td></tr><tr><td><strong><em>includeEmptyResults</em></strong></td><td>No</td><td>Same as <strong><em>groupByProperty</em></strong>, but <strong><em>&#8220;Yes&#8221;</em></strong> will also show empty ranges</td></tr><tr><td><strong><em>seriesProperty</em></strong></td><td>No</td><td>Same as <strong><em>groupByProperty</em></strong></td></tr></tbody></table></figure>



<h4 id="h-2-4-2-how-boundaries-work" class="wp-block-heading">2.4.2. How boundaries work</h4>



<p class="wp-block-paragraph">Each boundary value marks the inclusive lower bound of a range and the exclusive upper bound of the range below it, except for the last range, which is fully inclusive. For example, <code>["0", "1000", "5000", "10000"]</code> creates three buckets: <code>[0, 1000)</code>, <code>[1000, 5000)</code> and <code>[5000, 10000]</code>. This means that a value of &#8220;1000&#8221; will end-up on the 2nd bucket only. A value of &#8220;5000&#8221; or &#8220;10000&#8221; will end-up on the 3rd bucket.</p>



<p class="wp-block-paragraph">In addition, as you can see in the examples above, you can use a wildcard (<code>"*"</code>) on either end: a leading <code>"*"</code> creates a bucket for everything below the first boundary, and a trailing <code>"*"</code> creates a bucket for everything at or above the last boundary.</p>



<h3 id="h-2-5-list-one-row-per-object" class="wp-block-heading">2.5. list &#8211; one row per object</h3>



<p class="wp-block-paragraph"><strong><em>list</em></strong> are pretty similar to search results from M-Files, in the sense that it will just list something, with pre-defined columns. When using this aggregation type, the reducers have no effect, because it only lists objects but do not apply any modifications / computing on them.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
// List all contracts with 3 specific columns
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;list&quot;,
  &quot;displayProperties&quot;: &#x5B;&quot;Agreement type&quot;, &quot;Effective through&quot;, &quot;Responsible person&quot;]
}
</pre></div>


<figure class="wp-block-table"><table><thead><tr><th>Field</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><em>displayProperties</em></strong></td><td>No, but highly recommended</td><td>Name of properties to include in the table, as columns, in addition to the object name</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">For simple lists, the objects will be pre-sorted alphabetically but the user is then able to re-sort them, by each of the columns displayed. When <strong><em>drillThroughEnabled</em></strong> is set to <strong><em>&#8220;Yes&#8221;</em></strong>, then table rows become clickable and allows navigation to the object in question. There is no modal/drill-through in this case, since the table list already display the target object (no grouping).</p>



<h2 id="h-3-the-six-reducers" class="wp-block-heading">3. The six reducers</h2>



<p class="wp-block-paragraph">As mentioned, all five aggregation types, except <strong><em>list</em></strong>, accept a <strong><em>reducer</em></strong>. You probably understand them already, but just as a quick table:</p>



<figure class="wp-block-table"><table><thead><tr><th>Reducer</th><th>What it computes</th><th>reducerProperty types</th><th>Empty set returns</th></tr></thead><tbody><tr><td><strong><em>count</em></strong> <em>(default)</em></td><td>Number of matching objects</td><td>n/a</td><td><strong><em>0</em></strong></td></tr><tr><td><strong><em>sum</em></strong></td><td>Total of the property&#8217;s values</td><td>Numeric or time</td><td><strong><em>null</em></strong> (rendered as <strong><em>&#8211;</em></strong>)</td></tr><tr><td><strong><em>avg</em></strong></td><td>Arithmetic mean</td><td>Numeric or time</td><td><strong><em>null</em></strong> (rendered as <strong><em>&#8211;</em></strong>)</td></tr><tr><td><strong><em>median</em></strong></td><td>Middle value</td><td>Numeric or time</td><td><strong><em>null</em></strong> (rendered as <strong><em>&#8211;</em></strong>)</td></tr><tr><td><strong><em>min</em></strong></td><td>Smallest value</td><td>Numeric or date / timestamp / time</td><td><strong><em>null</em></strong> (rendered as <strong><em>&#8211;</em></strong>)</td></tr><tr><td><strong><em>max</em></strong></td><td>Largest value</td><td>Numeric or date / timestamp / time</td><td><strong><em>null</em></strong> (rendered as <strong><em>&#8211;</em></strong>)</td></tr></tbody></table></figure>



<h3 id="h-3-1-the-date-time-valued-reducer-rule" class="wp-block-heading">3.1. The date/time-valued reducer rule</h3>



<p class="wp-block-paragraph">When <strong><em>min</em></strong> or <strong><em>max</em></strong> is applied to a date, timestamp, or time property, the reducer returns the value and it will be formatted for end-users based on their localization / regional settings: e.g. <strong><em>DD/MM/YYYY</em></strong> for dates, <strong><em>DD/MM/YYYY HH:mm</em></strong> for timestamps (minute precision), and <strong><em>HH:mm:ss</em></strong> for times.</p>



<p class="wp-block-paragraph">When <strong><em>sum</em></strong>, <strong><em>avg</em></strong> or <strong><em>median</em></strong> is applied to a time property, it allows you to compute a total (or average/median) duration for a certain activity. This might be useful if you have time-constraints.</p>



<p class="wp-block-paragraph">Three widgets can render date/time values: <strong><em>kpiNumber</em></strong> (a single big number), <strong><em>gauge</em></strong> (switch to date mode, covered in <a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/" target="_blank" rel="noreferrer noopener">Post 4a</a>) and <strong><em>table</em></strong> (simple display in rows).</p>



<h2 id="h-4-seriesproperty-the-second-dimension" class="wp-block-heading">4. seriesProperty &#8211; the second dimension</h2>



<p class="wp-block-paragraph">The series details were already covered a bit in the posts <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" target="_blank" rel="noreferrer noopener">4b</a> and <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/" target="_blank" rel="noreferrer noopener">4c</a>. But, this is the feature that turns a single-series widget into a multi-actor comparison. When you set a <strong><em>seriesProperty</em></strong> on a <strong><em>groupBy</em></strong> aggregation, the engine will automatically produce one series per distinct value of the series property. This allows a two-dimensional comparison.</p>



<p class="wp-block-paragraph">The behavior per widget type is the following:</p>



<ul class="wp-block-list">
<li><strong><em>line</em></strong> / <strong><em>area</em></strong> &#8211; one colored line per series, with an auto-generated legend.</li>



<li><strong><em>bar</em></strong> &#8211; one colored sub-bar per series (<strong><em>display.barLayout</em></strong> controls stacked vs grouped).</li>



<li><strong><em>donut</em></strong> &#8211; a multi-mini-pie grid, one donut per series, with a shared legend.</li>



<li><strong><em>table</em></strong> &#8211; a cross-tab pivot, one column per series.</li>
</ul>



<p class="wp-block-paragraph">As a reminder, <strong><em>kpiNumber</em></strong> and <strong><em>gauge</em></strong> ignore <strong><em>seriesProperty</em></strong>, since they only display single-values.</p>



<h3 id="h-4-1-use-low-cardinality-series-only" class="wp-block-heading">4.1. Use low-cardinality series only</h3>



<p class="wp-block-paragraph">I repeated this in every widget post but it is worth restating: <strong><em>seriesProperty</em></strong> should be <strong>low-cardinality</strong>. A property with five distinct values produces a readable multi-series chart. A property with two hundred values produces a colored mess. In that case, you might want to use a <strong><em>groupByRange</em></strong>, to reduce the amount of groups and therefore the series.</p>



<h3 id="h-4-2-includeemptyresults-in-multi-series" class="wp-block-heading">4.2. includeEmptyResults in multi-series</h3>



<p class="wp-block-paragraph">In single-series mode, <strong><em>includeEmptyResults: &#8220;Yes&#8221;</em></strong> fills empty time buckets with zero values. In multi-series mode, it does the same <strong>across both dimensions</strong>: every series gets a zero in any bucket where it has no data. This avoids broken lines and visually confusing gaps.</p>



<p class="wp-block-paragraph">The behavior also adds a <strong><em>(none)</em></strong> bucket for objects whose primary group property has no value, and a <strong><em>(none)</em></strong> series for objects whose series property has no value. These are appended at the end so it doesn&#8217;t disrupt the &#8220;main story&#8221;.</p>



<h3 id="h-4-3-multi-select-lookups-in-seriesproperty-and-propertyname" class="wp-block-heading">4.3. Multi-select lookups in seriesProperty (and propertyName)</h3>



<p class="wp-block-paragraph">A subtle but important case: as you probably know, M-Files has a <strong>multi-select lookup</strong> property type. These allow the selection of multiple pre-defined values. Because of that, objects with multiple values will end-up in multiple buckets/groups, with the &#8220;counted once per value&#8221; rule.</p>



<p class="wp-block-paragraph">Example: a customer with office locations in Geneva, Zurich and Berlin could appear three times, once for a Geneva bucket, once for a Zurich bucket and finally once for a Berlin bucket. The total of all bucket counts can therefore exceed the total number of objects when some objects have multiple values.</p>



<p class="wp-block-paragraph">This is the correct and expected behavior for multi-select lookups. The alternative (counting each object only in its first value) would silently hide the multi-value relationships that often matter most.</p>



<h2 id="h-5-displayproperties-on-drill-through" class="wp-block-heading">5. displayProperties on drill-through</h2>



<p class="wp-block-paragraph">I mentioned <strong><em>displayProperties</em></strong> in section 2.5 above, as optional column to be added for <strong><em>list</em></strong> aggregations. The same field has a second role for all other aggregation type (<strong><em>summary</em></strong>, <strong><em>groupByProperty</em></strong>, <strong><em>groupByDateBucket</em></strong>, <strong><em>groupByRange</em></strong>): it controls <strong>the columns in the drill-through modal</strong>.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByProperty&quot;,
  &quot;propertyName&quot;: &quot;Agreement type&quot;,
  &quot;displayProperties&quot;: &#x5B;&quot;Effective through&quot;, &quot;Responsible person&quot;]
}
</pre></div>


<p class="wp-block-paragraph">When the user clicks a donut slice (or a bar, or a row in a count table), the drill-through modal shows one row per object in that group, with the object name plus all the optional columns defined in the <strong><em>displayProperties</em></strong> value (e.g. <strong><em>Effective through</em></strong> and <strong><em>Responsible person</em></strong>).</p>



<h2 id="h-6-the-compatibility-cheat-sheet" class="wp-block-heading">6. The compatibility cheat sheet</h2>



<p class="wp-block-paragraph">I already put this table at the end of <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/" target="_blank" rel="noreferrer noopener">Post 4c</a>, but it is short enough, so:</p>



<figure class="wp-block-table"><table><thead><tr><th>Widget type</th><th>summary</th><th>groupByProperty</th><th>groupByDateBucket</th><th>groupByRange</th><th>list</th><th>seriesProperty</th></tr></thead><tbody><tr><td><strong><em>kpiNumber</em></strong></td><td>yes</td><td>&#8211;</td><td>&#8211;</td><td>&#8211;</td><td>&#8211;</td><td>ignored</td></tr><tr><td><strong><em>gauge</em></strong></td><td>yes</td><td>&#8211;</td><td>&#8211;</td><td>&#8211;</td><td>&#8211;</td><td>ignored</td></tr><tr><td><strong><em>donut</em></strong></td><td>&#8211;</td><td>yes</td><td>yes</td><td>yes</td><td>&#8211;</td><td>multi-mini-pie</td></tr><tr><td><strong><em>bar</em></strong></td><td>&#8211;</td><td>yes</td><td>yes</td><td>yes</td><td>&#8211;</td><td>stacked or grouped</td></tr><tr><td><strong><em>line</em></strong></td><td>&#8211;</td><td>yes</td><td>yes</td><td>yes</td><td>&#8211;</td><td>multi-series lines</td></tr><tr><td><strong><em>area</em></strong></td><td>&#8211;</td><td>yes</td><td>yes</td><td>yes</td><td>&#8211;</td><td>multi-series areas</td></tr><tr><td><strong><em>table</em></strong></td><td>yes</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td><td>cross-tab pivot</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Combine this with the reducer table in section 3 and you have the full answer to &#8220;can I use aggregation X with widget Y, and with reducer Z on property type T&#8221;. In any case, the Visual Designer and the validation process will prevent you to make any mistake.</p>



<h2 id="h-7-what-this-gives-you" class="wp-block-heading">7. What this gives you</h2>



<p class="wp-block-paragraph">The query side (<a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/" target="_blank" rel="noreferrer noopener">Post 5</a>) plus the aggregation side (this post) together cover everything the engine supports. If that wasn&#8217;t the case before, you should now be able to read and understand every line from any of the previous JSON example.</p>



<p class="wp-block-paragraph">The combinations are richer than they look at first. A <strong><em>groupByDateBucket</em></strong> on monthly invoices with a <strong><em>sum</em></strong> reducer on <strong><em>Amount</em></strong> and a <strong><em>seriesProperty</em></strong> on <strong><em>Customer</em></strong>, rendered as a <strong><em>line</em></strong> widget with <strong><em>includeEmptyResults: &#8220;Yes&#8221;</em></strong>, gives a multi-customer revenue trend that takes about 15 lines of JSON. A <strong><em>groupByProperty</em></strong> on <strong><em>Agreement type</em></strong> with a <strong><em>seriesProperty</em></strong> on <strong><em>Workflow state</em></strong> rendered as a <strong><em>table</em></strong> gives a cross-tab pivot with drill-through on every cell.</p>



<p class="wp-block-paragraph">The possibilities aren&#8217;t endless, obviously, but good luck if you would like to try them all&#8230; Last time I checked, you could create several million different widget combinations. An important part of that would trigger warnings or errors for non-supported cases, but still a considerable scale.</p>



<p class="wp-block-paragraph">So far, I covered the end-user part, the JSON, the widgets and now the queries. These are the building blocks of dashboards. The remaining posts of the series will cover:</p>



<ul class="wp-block-list">
<li><a href="https://www.dbi-services.com/blog/m-files-bd-the-admin-part/" id="45312" target="_blank" rel="noreferrer noopener"><strong>Post 7</strong></a> &#8211; The Admin tab: the actions, Visual Designer / JSON editor, the two-stage validator, import / export, access control.</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-putting-it-together-with-the-visual-designer/" id="45433" target="_blank" rel="noreferrer noopener">Post 8</a></strong> &#8211; Building a dashboard with the Visual Designer.</li>
</ul>



<p class="wp-block-paragraph">Want to know more about this Business Dashboard? <a href="https://www.dbi-services.com/company/contact/" target="_blank" rel="noreferrer noopener">Contact us</a> and we will be happy to showcase it on <a href="https://www.m-files.com/" target="_blank" rel="noreferrer noopener">M-Files</a>.</p>



<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/">M-Files BD &#8211; Aggregations, reducers and series</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>M-Files BD &#8211; Queries: objectType, class, filters, date tokens</title>
		<link>https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/</link>
					<comments>https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/#respond</comments>
		
		<dc:creator><![CDATA[Morgan Patou]]></dc:creator>
		<pubDate>Sat, 20 Jun 2026 16:32:18 +0000</pubDate>
				<category><![CDATA[Enterprise content management]]></category>
		<category><![CDATA[Business Dashboard]]></category>
		<category><![CDATA[Class]]></category>
		<category><![CDATA[dateToken]]></category>
		<category><![CDATA[Filters]]></category>
		<category><![CDATA[M-Files]]></category>
		<category><![CDATA[objectType]]></category>
		<category><![CDATA[query]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=45175</guid>

					<description><![CDATA[<p>In the previous posts of this series, I covered the anatomy of a dashboard definition and the seven widget types (KPI &#38; gauge, line &#38; area, donut, bar &#38; table) the engine supports, as of now. In this one, we will go through the Business Dashboard queries, i.e. the query section that I pretty much [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/">M-Files BD &#8211; Queries: objectType, class, filters, date tokens</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In the previous posts of this series, I covered the <a href="https://www.dbi-services.com/blog/m-files-bd-anatomy-of-a-dashboard-definition/" target="_blank" rel="noreferrer noopener">anatomy of a dashboard definition</a> and the seven widget types (<a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/" target="_blank" rel="noreferrer noopener">KPI &amp; gauge</a>, <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" target="_blank" rel="noreferrer noopener">line &amp; area</a>, <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/" target="_blank" rel="noreferrer noopener">donut, bar &amp; table</a>) the engine supports, as of now. In this one, we will go through the Business Dashboard queries, i.e. the <strong><em>query</em></strong> section that I pretty much ignores so far. It is now time to look at it a bit more.</p>



<p class="wp-block-paragraph">A query has four parts: <strong><em>objectType</em></strong>, <strong><em>class</em></strong>, <strong><em>filters</em></strong>, and <strong><em>aggregation</em></strong>. This post covers the first three. The fourth (aggregations, including its reducers) is the topic of the next post.</p>



<p class="wp-block-paragraph">As mentioned before, the principle that drove the design of the Business Dashboard is to <strong>be generic</strong>. The engine never enumerates specific business values, it only translates the structured JSON query into a standard M-Files server search.</p>



<h2 id="h-1-objecttype-the-m-files-object-type-to-query" class="wp-block-heading">1. objectType &#8211; the M-Files object type to query</h2>



<p class="wp-block-paragraph">Every query starts with <strong><em>objectType</em></strong>. It is the M-Files object type the engine searches against, addressed by its display name (the singular form is fine, the engine resolves it and uses it).</p>



<h3 id="h-1-1-single-object-type" class="wp-block-heading">1.1. Single object type</h3>



<p class="wp-block-paragraph">The simplest form is a string:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;objectType&quot;: &quot;Document&quot;
</pre></div>


<p class="wp-block-paragraph">Or any other object type defined in your vault:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
// Looking for Customers
&quot;objectType&quot;: &quot;Customer&quot;

// Looking for Projects
&quot;objectType&quot;: &quot;Project&quot;

// Looking for Employees
&quot;objectType&quot;: &quot;Employee&quot;
</pre></div>


<p class="wp-block-paragraph">The display name is <strong>case-insensitive</strong> but must otherwise match what the vault shows. Please note that, if the vault is let&#8217;s say, in German, you might need to use &#8220;Dokument&#8221; and not &#8220;Document&#8221;. In short, you need to use what the vault defines/shows.</p>



<p class="wp-block-paragraph">If the name does not match, the widget shows a clear <strong><em>✗ Object type &#8216;xxx&#8217; not found in this vault</em></strong> error.</p>



<h3 id="h-1-2-multiple-object-types" class="wp-block-heading">1.2. Multiple object types</h3>



<p class="wp-block-paragraph">When the same logical entity exists across more than one M-Files object type (for example, a &#8220;Proposal&#8221; can be both a Document and a Document collection), you can pass an array:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;objectType&quot;: &#x5B;&quot;Document&quot;, &quot;Document collection&quot;]
</pre></div>


<p class="wp-block-paragraph">The engine resolves every name and builds a single search to find all matching objects. This behaves like the <strong><em>&#8220;is one of&#8221;</em></strong> filter in M-Files Advanced Search. Please note that the Drill-through will also inherit the same scope, automatically.</p>



<p class="wp-block-paragraph">A small but important detail: even in this case, the engine executes a single search. This matters for performance and for consistency (a single <strong><em>serverScanMaxResults</em></strong> cap applies, not two independent caps).</p>



<h2 id="h-2-class-narrowing-the-scope-and-the-perf-impact" class="wp-block-heading">2. class &#8211; narrowing the scope (and the perf impact)</h2>



<p class="wp-block-paragraph">The <strong><em>class</em></strong> field is optional but <strong><em>strongly</em></strong> recommended. It restricts the query to one specific class:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;objectType&quot;: &quot;Document&quot;,
&quot;class&quot;: &quot;Contract or Agreement&quot;
</pre></div>


<p class="wp-block-paragraph">Querying &#8220;all Documents&#8221; in a vault that contains 50&#8217;000 documents of which only 1&#8217;000 are Contracts is pure waste. Adding <strong><em>&#8220;class&#8221;: &#8220;Contract or Agreement&#8221;</em></strong> narrows the scan immediately, which both improves performance and makes the widget result more meaningful.</p>



<p class="wp-block-paragraph">Therefore, both M-Files Administrators and M-Files Users will thank you for selecting the right class to use. It avoids slowness, irrelevant results and reduces resource usage. You should always set a <strong><em>class</em></strong>, when possible.</p>



<p class="wp-block-paragraph">When <strong><em>objectType</em></strong> is an array and <strong><em>class</em></strong> is specified, the engine will find any object, from that class, from any of the object types listed. So the following will match Proposal-class objects from both types:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;objectType&quot;: &#x5B;&quot;Document&quot;, &quot;Document collection&quot;],
&quot;class&quot;: &quot;Proposal&quot;
</pre></div>


<p class="wp-block-paragraph">If the class is misspelled, the widget shows a <strong><em>✗ Class &#8216;xxx&#8217; not found on the specified object type(s)</em></strong> error. As with object types, the name depends on what the vault defines/shows.</p>



<h2 id="h-3-filters-the-and-combined-conditions" class="wp-block-heading">3. filters &#8211; the AND-combined conditions</h2>



<p class="wp-block-paragraph">The <strong><em>filters</em></strong> array, which is optional, contains zero or more conditions. If you specify multiple filters, then <strong>all filters must match</strong>. That means that M-Files applies a AND semantic between each filter. By the way, M-Files doesn&#8217;t directly support &#8220;OR&#8221; conditions, it always joins them. The only &#8220;kind-of-an-exception&#8221;, as far as I know, is the <strong><em>&#8220;is one of&#8221;</em></strong> where you can set multiple values from the Lookup/MultiSelectLookup property values. But that&#8217;s only a single condition, not multiple conditions.</p>



<p class="wp-block-paragraph">Here is an example of a filter:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;filters&quot;: &#x5B;
  { &quot;property&quot;: &quot;Effective through&quot;, &quot;operator&quot;: &quot;greaterOrEqual&quot;,
    &quot;value&quot;: &quot;@today&quot;, &quot;valueType&quot;: &quot;dateToken&quot; },
  { &quot;property&quot;: &quot;Agreement type&quot;, &quot;operator&quot;: &quot;equals&quot;,
    &quot;value&quot;: &quot;Subcontracting Agreement&quot; }
]
</pre></div>


<p class="wp-block-paragraph">Each filter has up to four fields:</p>



<ul class="wp-block-list">
<li><strong><em>property</em></strong>: the property display name on which to apply the operator / value.</li>



<li><strong><em>operator</em></strong>: one of the operators listed in section 4 below.</li>



<li><strong><em>value</em></strong>: the value to use for the comparison with the property&#8217;s actual value. For the four &#8220;presence&#8221; operators (<strong><em>isEmpty</em></strong>, <strong><em>isNotEmpty</em></strong>, <strong><em>isPresentEmpty</em></strong>, <strong><em>isPresentNotEmpty</em></strong>), this parameter should NOT be defined. For the &#8220;begin-end&#8221; operators (<strong><em>between</em></strong> / <strong><em>notBetween</em></strong>, this parameter should be an array of two elements. For the &#8220;is one of&#8221; operators (<strong><em>inList</em></strong> / <strong><em>notInList</em></strong>), this parameter should be an array of at least two elements. Finally, in all other cases, it&#8217;s simply a string.</li>



<li><strong><em>valueType</em></strong>: either <strong><em>&#8220;literal&#8221;</em></strong> (default, it means to use the value as-is) or a <strong><em>&#8220;dateToken&#8221;</em></strong> (covered in section 5).</li>
</ul>



<h2 id="h-4-the-filter-operators" class="wp-block-heading">4. The filter operators</h2>



<p class="wp-block-paragraph">The engine supports a rich set of operators across two execution paths:</p>



<ul class="wp-block-list">
<li><strong>Native</strong>: runs server-side before the <strong><em>serverScanMaxResults</em></strong> cap is applied. Therefore, this is the preferred option, when possible.</li>



<li><strong>Post-filter</strong>: runs in memory after the server returns up to <strong><em>serverScanMaxResults</em></strong> objects. Therefore, this is correct and useful when you want to do something that M-Files doesn&#8217;t support out-of-the-box but that still makes sense for your Business use-case.</li>
</ul>



<p class="wp-block-paragraph">In short, whenever possible, prefer to apply a <strong><em>native</em></strong> filter. The main reason for this is simple. Let&#8217;s assume that you have 1&#8217;000 Contracts in the vault:</p>



<ul class="wp-block-list">
<li>If you apply a <strong><em>native</em></strong> filter, for example <strong><em>Effective through &gt;= @today</em></strong>, then if only 200 of them match that criteria, then M-Files will only return these 200 objects directly. There is no loss of performance here.</li>



<li>If you apply a <strong><em>post-filter</em></strong>, for example <strong><em>Effective through contains 2026-12</em></strong>, then M-Files will have no other choice than returning all 1&#8217;000 Contracts first. On top of that, the post-filter compares the value of Effective through and checks whether it contains &#8220;2026-12&#8221;. This is because the &#8220;contains&#8221; condition doesn&#8217;t exist in M-Files for Date properties. Therefore, we cannot use what doesn&#8217;t exist, but we still want to provide that possibility / logic, and therefore it is done in memory after returning all results. In that example, you get 5x more results and on top of it, you also need to check which ones match the expected value.</li>
</ul>



<p class="wp-block-paragraph">The performance difference between <strong><em>native</em></strong> and <strong><em>post-filter</em></strong> is imperceptible for small result sets (&lt;few hundreds), but you would probably feel it if you expect to fetch thousands of results. A great catch, if you need to apply a post-filter for a valid business reasons, is to first apply a native one, which highly reduces the result set, and then apply the post-filter one that you need. Also, don&#8217;t forget to specify a <strong><em>class</em></strong>!</p>



<p class="wp-block-paragraph">Then, let&#8217;s proceed with a deep-dive on the different operators (if you only want the &#8220;summary&#8221;, look at the end of section 4 for the cheat sheet). The 22 operators support all data types, without exceptions, contrary to M-Files operators which offer much less capabilities. The only distinction is, as mentioned above, whether the operator is native or post-filter.</p>



<h3 id="h-4-1-equals-and-notequal" class="wp-block-heading">4.1. equals and notEqual</h3>



<p class="wp-block-paragraph">As you would expect, these are for exact match. Only the MultiLineText properties are processed as post-filter.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Agreement type&quot;, &quot;operator&quot;: &quot;equals&quot;,
  &quot;value&quot;: &quot;Subcontracting Agreement&quot; }

{ &quot;property&quot;: &quot;Customer&quot;, &quot;operator&quot;: &quot;notEqual&quot;,
  &quot;value&quot;: &quot;ESTT Corporation (IT)&quot; }
</pre></div>


<p class="wp-block-paragraph">When using display names on Lookup properties (Lookup or MultiSelectLookup), as its the case in above example, the value lists are cached, so that each execution doesn&#8217;t need to re-fetch the things that it already knows of, and it can just use it directly, to ask M-Files what&#8217;s the updated count.</p>



<h3 id="h-4-2-lessthan-greaterthan-lessorequal-greaterorequal" class="wp-block-heading">4.2. lessThan, greaterThan, lessOrEqual, greaterOrEqual</h3>



<p class="wp-block-paragraph">These &#8220;range&#8221; operators support all property types except Boolean, because it doesn&#8217;t make any sense to apply a &#8220;lessThan&#8221; to a Boolean&#8230; In addition, and similarly to above, only the MultiLineText properties are processed as post-filter, everything else is native. Obviously, you can apply these operators on Date and Numeric values, but it also works with Text, Lookup or MultiSelectLookup. When it needs to work on Text-based properties, it does a lexicographic comparison (e.g. ABC &lt; ABD, ACE &gt; ABB).</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Due date&quot;, &quot;operator&quot;: &quot;lessThan&quot;,
  &quot;value&quot;: &quot;@today&quot;, &quot;valueType&quot;: &quot;dateToken&quot; }

{ &quot;property&quot;: &quot;Amount&quot;, &quot;operator&quot;: &quot;greaterOrEqual&quot;, &quot;value&quot;: 1000 }
</pre></div>


<h3 id="h-4-3-between-and-notbetween" class="wp-block-heading">4.3. between and notBetween</h3>



<p class="wp-block-paragraph">These as also &#8220;range&#8221; operators but I put them separately because the <strong><em>value</em></strong> must be a two-element array, as previously written. <strong><em>between</em></strong> is, otherwise, exactly the same as lessThan/greaterThan/lessOrEqual/greaterOrEqual. In the array, you would provide the begin and the end of the range. to fetch.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Effective through&quot;, &quot;operator&quot;: &quot;between&quot;,
  &quot;value&quot;: &#x5B;&quot;@startOfYear&quot;, &quot;@endOfYear&quot;], &quot;valueType&quot;: &quot;dateToken&quot; }

{ &quot;property&quot;: &quot;Amount&quot;, &quot;operator&quot;: &quot;between&quot;, &quot;value&quot;: &#x5B;100, 1000] }
</pre></div>


<p class="wp-block-paragraph"><strong><em>notBetween</em></strong> is the opposite of <strong><em>between</em></strong>, obviously, but it&#8217;s a bit more than that&#8230; As previously mentioned, M-Files does NOT handle <strong><em>OR</em></strong> logic. But if you think about it, a &#8220;notBetween&#8221; is actually an OR, because you want values below the range OR above the range (<strong><em>value &lt; low OR value &gt; high</em></strong>). Because of that, <strong><em>notBetween</em></strong> is always a <strong>post-filter</strong>, without exception.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Amount&quot;, &quot;operator&quot;: &quot;notBetween&quot;, &quot;value&quot;: &#x5B;100, 1000] }
</pre></div>


<h3 id="h-4-4-isempty-isnotempty-ispresentempty-ispresentnotempty" class="wp-block-heading">4.4. isEmpty, isNotEmpty, isPresentEmpty, isPresentNotEmpty</h3>



<p class="wp-block-paragraph">These four operators test whether a property has a value, as the name suggests&#8230; However, there is a catch: M-Files only distinguish between &#8220;the property is present and empty&#8221; or &#8220;the property is present and non-empty&#8221;.</p>



<p class="wp-block-paragraph">You might have faced that with Templates for example. That&#8217;s a pretty common occurrence. In M-Files, there is a parameter &#8220;Is template&#8221; which indicates whether a specific object has been defined as a template. But that parameter is only present in a few select objects, it&#8217;s usualy not present on all of them. Therefore, if you search for &#8220;Is template &#8211; is empty&#8221;, you will most probably find 0 results, because M-Files only check for documents where the property is present and where it is empty.</p>



<p class="wp-block-paragraph">The distinction is simple but it matters a lot. That&#8217;s why in the Business Dashboard, the <strong><em>native</em></strong> M-Files &#8220;is empty&#8221; / &#8220;is not empty&#8221; have been renamed as <strong><em>isPresentEmpty</em></strong> and <strong><em>isPresentNotEmpty</em></strong>. These are the fully native option from M-Files, and therefore they run on the server-side.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Effective through&quot;, &quot;operator&quot;: &quot;isPresentEmpty&quot; }

{ &quot;property&quot;: &quot;Responsible person&quot;, &quot;operator&quot;: &quot;isPresentNotEmpty&quot; }
</pre></div>


<p class="wp-block-paragraph">In addition, I also defined two other operators, <strong><em>isEmpty</em></strong> and <strong><em>isNotEmpty</em></strong>. These two are <strong>always post-filters</strong>, they scan all matching objects in memory after the server returns them:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Effective through&quot;, &quot;operator&quot;: &quot;isEmpty&quot; }

{ &quot;property&quot;: &quot;Responsible person&quot;, &quot;operator&quot;: &quot;isNotEmpty&quot; }
</pre></div>


<p class="wp-block-paragraph">Because of that post-processing, <strong><em>isEmpty</em></strong> matches objects where the property is absent OR where it is present but empty. This allows a more &#8220;complete&#8221; result set, which might be required in some specific business use-cases.</p>



<p class="wp-block-paragraph"><strong><em>isNotEmpty</em></strong> matches objects where the property is present AND non-empty. You might think it is exactly the same as <strong><em>isPresentNotEmpty</em></strong>, right? Well, in 99% of the cases, yes it is the same (but slower, since done as post-filter)&#8230; Except when there are bugs in M-Files ;). While developing the Business Dashboard, I found a few bugs, including one with lexicographic processing on MultiSelectLookup properties. The usage of my own operator was giving me a slightly different result and while investigating why, I found the reason and the bug in M-Files&#8217;s own operator.</p>



<p class="wp-block-paragraph">In summary:</p>



<figure class="wp-block-table"><table><thead><tr><th>What you want to find</th><th>Operator</th></tr></thead><tbody><tr><td>Objects where the property is present and empty OR is completely absent</td><td><strong><em>isEmpty</em></strong> (post-filter)</td></tr><tr><td>Objects where the property is present and empty</td><td><strong><em>isPresentEmpty</em></strong> (native)</td></tr><tr><td>Objects where the property has a value</td><td><strong><em>isPresentNotEmpty</em></strong> (native)</td></tr><tr><td>Objects where the property has a value</td><td><strong><em>isNotEmpty</em></strong> (post-filter alternative &#8211; sometimes more accurate)</td></tr></tbody></table></figure>



<h3 id="h-4-5-inlist-and-notinlist" class="wp-block-heading">4.5. inList and notInList</h3>



<p class="wp-block-paragraph">As the name suggests, if you are looking for multiple values, <strong><em>inList</em></strong> is what you should use. It must be an array of at least 2 elements, but you can put 50 if you want to. M-Files only support Lookup for this one, natively. Therefore, all other property types are handled as post-filters.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Agreement type&quot;, &quot;operator&quot;: &quot;inList&quot;,
  &quot;value&quot;: &#x5B;&quot;Subcontracting Agreement&quot;, &quot;Project Agreement&quot;] }

{ &quot;property&quot;: &quot;Workflow state&quot;, &quot;operator&quot;: &quot;inList&quot;,
  &quot;value&quot;: &#x5B;&quot;In review&quot;] }
</pre></div>


<p class="wp-block-paragraph"><strong><em>notInList</em></strong> is simply the opposite, so it just excludes all elements provided. It supports exactly the same thing as inList and works in the same way too.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Workflow state&quot;, &quot;operator&quot;: &quot;notInList&quot;,
  &quot;value&quot;: &#x5B;&quot;Approved&quot;] }
</pre></div>


<p class="wp-block-paragraph">A subtle catch with &#8220;not&#8221; operators (<strong><em>notInList</em></strong> and similar): an object whose Lookup property is present but has no item selected satisfies &#8220;not in list&#8221; by default, because there is no value to match or compare with. If you don&#8217;t want to see these objects, then you can simply add a second filter on that same property with <strong><em>isPresentNotEmpty</em></strong>!</p>



<h3 id="h-4-6-contains-and-doesnotcontain" class="wp-block-heading">4.6. contains and doesNotContain</h3>



<p class="wp-block-paragraph"><strong><em>contains</em></strong> is a substring match (similar to <strong><em>matchesWildcardPattern</em></strong> (c.f. below) with implicit wildcards on both sides), it finds objects where the property value includes the given string anywhere inside.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Title&quot;, &quot;operator&quot;: &quot;contains&quot;, &quot;value&quot;: &quot;NDA&quot; }

{ &quot;property&quot;: &quot;Title&quot;, &quot;operator&quot;: &quot;doesNotContain&quot;,
  &quot;value&quot;: &quot;draft&quot; }
</pre></div>


<p class="wp-block-paragraph">For Text, MultiLineText and Lookup properties, both operators are <strong>native</strong>. The rest is supported as <strong>post-filters</strong>, comparing against the string representation.</p>



<h3 id="h-4-7-startswith-doesnotstartwith-endswith-doesnotendwith" class="wp-block-heading">4.7. startsWith, doesNotStartWith, endsWith, doesNotEndWith</h3>



<p class="wp-block-paragraph"><strong><em>startsWith</em></strong> and <strong><em>doesNotStartWith</em></strong> match objects based on the beginning of a property&#8217;s value. These are native only for Text and Lookup properties.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Name or title&quot;, &quot;operator&quot;: &quot;startsWith&quot;,
  &quot;value&quot;: &quot;PO-&quot;    }

{ &quot;property&quot;: &quot;Name or title&quot;, &quot;operator&quot;: &quot;doesNotStartWith&quot;,
  &quot;value&quot;: &quot;DRAFT-&quot; }
</pre></div>


<p class="wp-block-paragraph"><strong><em>endsWith</em></strong> and <strong><em>doesNotEndWith</em></strong> match based on the end of a property&#8217;s value. This doesn&#8217;t exist in M-Files, there is no equivalent and therefore, these are always <strong>post-filters</strong> for all property types.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Name or title&quot;, &quot;operator&quot;: &quot;endsWith&quot;,
  &quot;value&quot;: &quot;(final)&quot; }

{ &quot;property&quot;: &quot;Name or title&quot;, &quot;operator&quot;: &quot;doesNotEndWith&quot;,
  &quot;value&quot;: &quot;(draft)&quot; }
</pre></div>


<h3 id="h-4-8-matcheswildcardpattern-and-doesnotmatchwildcardpattern" class="wp-block-heading">4.8. matchesWildcardPattern and doesNotMatchWildcardPattern</h3>



<p class="wp-block-paragraph">These are the most expressive pattern operators. The <strong><em>value</em></strong> is a regex string where <strong><em>\</em></strong>* matches any number of characters and <strong><em>?</em></strong> matches exactly one character. This is pretty similar to the contains/startsWith/endsWith (and their opposite), obviously, but it is a bit more powerful if you need to match an exact pattern that you know of.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Title&quot;, &quot;operator&quot;: &quot;matchesWildcardPattern&quot;,
  &quot;value&quot;: &quot;PO-????-2026&quot; }

{ &quot;property&quot;: &quot;Title&quot;, &quot;operator&quot;: &quot;doesNotMatchWildcardPattern&quot;,
  &quot;value&quot;: &quot;ID-*&quot; }
</pre></div>


<h3 id="h-4-9-complete-operator-reference" class="wp-block-heading">4.9. Complete operator reference</h3>



<p class="wp-block-paragraph">You reached this point, so it&#8217;s time to have a cheat sheet of all operators. In the below table:</p>



<ul class="wp-block-list">
<li><strong><em>Text</em></strong> and <strong><em>MultiLineText</em></strong> are NOT interchangeable</li>



<li>&#8220;Lookup&#8221; includes both <strong><em>Lookup</em></strong> and <strong><em>MultiSelectLookup</em></strong> (these ARE interchangeable &#8211; one exception for <strong><em>between</em></strong> on MultiSelectLookup because of a bug in M-Files &#8211; the one I mentioned above)</li>



<li>&#8220;Date&#8221; includes <strong><em>Date</em></strong>, <strong><em>Time</em></strong> and <strong><em>Timestamp</em></strong> (these ARE interchangeable &#8211; one exception for <strong><em>notEqual</em></strong> on <strong><em>Timestamp</em></strong> because of a bug in M-Files &#8211; not the same as the one mentioned above)</li>



<li>&#8220;Numeric&#8221; includes <strong><em>Integer</em></strong>, <strong><em>Integer64</em></strong> and <strong><em>Floating</em></strong> (these ARE interchangeable) (&#8220;integer&#8221;/&#8221;real&#8221;)</li>
</ul>



<figure class="wp-block-table"><table><thead><tr><th>Operator</th><th>Value</th><th>Native support for</th><th>Post-filter support for</th><th>Notes</th></tr></thead><tbody><tr><td><strong><em>equals</em></strong></td><td>string</td><td>Text, Lookup, Date, Numeric, Boolean</td><td>MultiLineText</td><td>Exact match</td></tr><tr><td><strong><em>notEqual</em></strong></td><td>string</td><td>Text, Lookup, Date, Numeric, Boolean</td><td>MultiLineText, <strong><em>Timestamp</em></strong></td><td>Excludes exact match. Timestamp is an exception because of a bug in M-Files</td></tr><tr><td><strong><em>lessThan</em></strong></td><td>string</td><td>Text, Lookup, Date, Numeric</td><td>MultiLineText</td><td>Boolean not supported</td></tr><tr><td><strong><em>greaterThan</em></strong></td><td>string</td><td>Text, Lookup, Date, Numeric</td><td>MultiLineText</td><td>Boolean not supported</td></tr><tr><td><strong><em>lessOrEqual</em></strong></td><td>string</td><td>Text, Lookup, Date, Numeric</td><td>MultiLineText</td><td>Boolean not supported</td></tr><tr><td><strong><em>greaterOrEqual</em></strong></td><td>string</td><td>Text, Lookup, Date, Numeric</td><td>MultiLineText</td><td>Boolean not supported</td></tr><tr><td><strong><em>between</em></strong></td><td>array (<strong><em>[&#8220;low&#8221;, &#8220;high&#8221;]</em></strong>)</td><td>Text, Lookup, Date, Numeric</td><td>MultiLineText</td><td>Two-element array, inclusive: <strong><em>low &lt;= value &lt;= high</em></strong>. Boolean not supported</td></tr><tr><td><strong><em>notBetween</em></strong></td><td>array (<strong><em>[&#8220;low&#8221;, &#8220;high&#8221;]</em></strong>)</td><td>&#8211;</td><td>Text, MultiLineText, Lookup, Date, Numeric</td><td>Two-element array, exclusive: <strong><em>value &lt; low OR value &gt; high</em></strong>. Boolean not supported</td></tr><tr><td><strong><em>inList</em></strong></td><td>array (<strong><em>[&#8220;a1&#8221;, &#8220;a2&#8221;, &#8220;a3&#8221;]</em></strong>)</td><td>Lookup</td><td>Text, MultiLineText, Date, Numeric, Boolean</td><td>&#8220;OR&#8221; logic</td></tr><tr><td><strong><em>notInList</em></strong></td><td>array (<strong><em>[&#8220;a1&#8221;, &#8220;a2&#8221;, &#8220;a3&#8221;]</em></strong>)</td><td>Lookup</td><td>Text, MultiLineText, Date, Numeric, Boolean</td><td>Inverse of <strong><em>inList</em></strong></td></tr><tr><td><strong><em>contains</em></strong></td><td>string</td><td>Text, MultiLineText, Lookup</td><td>Date, Numeric, Boolean</td><td>Implicit wildcards on both sides</td></tr><tr><td><strong><em>doesNotContain</em></strong></td><td>string</td><td>Text, MultiLineText, Lookup</td><td>Date, Numeric, Boolean</td><td>Inverse of <strong><em>contains</em></strong></td></tr><tr><td><strong><em>startsWith</em></strong></td><td>string</td><td>Text, Lookup</td><td>MultiLineText, Date, Numeric, Boolean</td><td>Prefix match</td></tr><tr><td><strong><em>doesNotStartWith</em></strong></td><td>string</td><td>Text, Lookup</td><td>MultiLineText, Date, Numeric, Boolean</td><td>Inverse of <strong><em>startsWith</em></strong></td></tr><tr><td><strong><em>endsWith</em></strong></td><td>string</td><td>&#8211;</td><td>All</td><td>Suffix match</td></tr><tr><td><strong><em>doesNotEndWith</em></strong></td><td>string</td><td>&#8211;</td><td>All</td><td>Inverse of <strong><em>endsWith</em></strong></td></tr><tr><td><strong><em>matchesWildcardPattern</em></strong></td><td>string</td><td>Text, Lookup</td><td>MultiLineText, Date, Numeric, Boolean</td><td><em><strong>*</strong> = any chars, <strong><em>?</em></strong> = one char, e.g. <strong><em>PO</em>-??-??-202?</strong></em></td></tr><tr><td><strong><em>doesNotMatchWildcardPattern</em></strong></td><td>string</td><td>Text, Lookup</td><td>MultiLineText, Date, Numeric, Boolean</td><td>Inverse of <strong><em>matchesWildcardPattern</em></strong></td></tr><tr><td><strong><em>isEmpty</em></strong></td><td>&#8211;</td><td>&#8211;</td><td>All</td><td>Property is absent OR (present AND empty)</td></tr><tr><td><strong><em>isNotEmpty</em></strong></td><td>&#8211;</td><td>&#8211;</td><td>All</td><td>Property is present AND non-empty</td></tr><tr><td><strong><em>isPresentEmpty</em></strong></td><td>&#8211;</td><td>All</td><td>&#8211;</td><td>Property is present AND empty</td></tr><tr><td><strong><em>isPresentNotEmpty</em></strong></td><td>&#8211;</td><td>All</td><td>&#8211;</td><td>Property is present AND non-empty</td></tr></tbody></table></figure>



<h3 id="h-4-10-pattern-operators-on-date-properties" class="wp-block-heading">4.10. Pattern operators on date properties</h3>



<p class="wp-block-paragraph">All pattern operators (<strong><em>contains</em></strong>, <strong><em>startsWith</em></strong>, <strong><em>endsWith</em></strong>, <strong><em>matchesWildcardPattern</em></strong> and their negations) work on date properties via the post-filter path, comparing against the ISO <strong><em>yyyy-MM-dd</em></strong> string representation (or <strong><em>yyyy-MM-dd HH:mm</em></strong> for timestamp or <strong><em>HH:mm:ss</em></strong> for time properties). This opens up some convenient patterns, even with <strong><em>&#8220;valueType&#8221;: &#8220;literal&#8221;</em></strong>:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Effective through&quot;, &quot;operator&quot;: &quot;startsWith&quot;,
  &quot;value&quot;: &quot;2026&quot; }

{ &quot;property&quot;: &quot;Effective through&quot;, &quot;operator&quot;: &quot;startsWith&quot;,
  &quot;value&quot;: &quot;2026-04&quot; }

{ &quot;property&quot;: &quot;Effective through&quot;, &quot;operator&quot;: &quot;endsWith&quot;,
  &quot;value&quot;: &quot;-31&quot; }

{ &quot;property&quot;: &quot;Effective through&quot;, &quot;operator&quot;: &quot;matchesWildcardPattern&quot;,
  &quot;value&quot;: &quot;2026-??-??&quot; }
</pre></div>


<p class="wp-block-paragraph">And with <strong><em>&#8220;valueType&#8221;: &#8220;dateToken&#8221;</em></strong>, the token resolves to a full ISO date used as the exact string pattern:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Effective through&quot;, &quot;operator&quot;: &quot;contains&quot;,
  &quot;value&quot;: &quot;@today&quot;, &quot;valueType&quot;: &quot;dateToken&quot; }

{ &quot;property&quot;: &quot;Effective through&quot;, &quot;operator&quot;: &quot;startsWith&quot;,
  &quot;value&quot;: &quot;@startOfMonth+5d&quot;, &quot;valueType&quot;: &quot;dateToken&quot; }
</pre></div>


<p class="wp-block-paragraph">A resolved token produces a full ISO date for the operators, c.f. next section for a deeper dive into dateToken details.</p>



<h2 id="h-5-date-tokens-relative-filters-that-always-make-sense" class="wp-block-heading">5. Date tokens &#8211; relative filters that always make sense</h2>



<p class="wp-block-paragraph">Hardcoding a date like <strong><em>&#8220;2026-01-01&#8221;</em></strong> in a filter works, but it ages badly. The dashboard built today shows different data on January 2nd than it did on December 31st, because the filter is now relative to a different &#8220;today&#8221;.</p>



<p class="wp-block-paragraph">Date tokens solve this. Any string value used with <strong><em>&#8220;valueType&#8221;: &#8220;dateToken&#8221;</em></strong> is resolved to an absolute date at query execution time, so the dashboard stays meaningful as time passes.</p>



<h3 id="h-5-1-anchor-tokens" class="wp-block-heading">5.1. Anchor tokens</h3>



<p class="wp-block-paragraph">The following anchors are recognized:</p>



<figure class="wp-block-table"><table><thead><tr><th>Token</th><th>Resolves to</th></tr></thead><tbody><tr><td><strong><em>@now</em></strong></td><td>Current date AND time</td></tr><tr><td><strong><em>@today</em></strong></td><td>Midnight of the current day</td></tr><tr><td><strong><em>@startOfDay</em></strong></td><td>Same as <strong><em>@today</em></strong></td></tr><tr><td><strong><em>@endOfDay</em></strong></td><td>23:59:59 of the current day</td></tr><tr><td><strong><em>@startOfWeek</em></strong></td><td>Monday of the current ISO week at 00:00:00</td></tr><tr><td><strong><em>@endOfWeek</em></strong></td><td>Sunday of the current ISO week at 23:59:59</td></tr><tr><td><strong><em>@startOfMonth</em></strong></td><td>First day of the current month at 00:00:00</td></tr><tr><td><strong><em>@endOfMonth</em></strong></td><td>Last day of the current month at 23:59:59</td></tr><tr><td><strong><em>@startOfQuarter</em></strong></td><td>First day of the current quarter at 00:00:00</td></tr><tr><td><strong><em>@endOfQuarter</em></strong></td><td>Last day of the current quarter at 23:59:59</td></tr><tr><td><strong><em>@startOfYear</em></strong></td><td>January 1st of the current year at 00:00:00</td></tr><tr><td><strong><em>@endOfYear</em></strong></td><td>December 31st of the current year at 23:59:59</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">So <strong><em>@startOfYear</em></strong>, at the time of writing this blog (i.e. in 2026) is <strong><em>2026-01-01 00:00:00</em></strong>. Starting from January 1st 2027, the same token will automatically resolve to <strong><em>2027-01-01 00:00:00</em></strong> instead. Therefore, the dashboard rolls forward automatically.</p>



<p class="wp-block-paragraph">If there is a need to add more date tokens, it&#8217;s always possible.</p>



<h3 id="h-5-2-offsets-days-hours-minutes-and-seconds" class="wp-block-heading">5.2. Offsets: days, hours, minutes, and seconds</h3>



<p class="wp-block-paragraph">Any anchor presented above can be followed by one or more offsets to add or subtract time:</p>



<figure class="wp-block-table"><table><thead><tr><th>Token</th><th>Meaning</th></tr></thead><tbody><tr><td><strong><em>@today-30d</em></strong></td><td>30 days ago</td></tr><tr><td><strong><em>@today+7d</em></strong></td><td>One week from today</td></tr><tr><td><strong><em>@startOfMonth+14d</em></strong></td><td>14 days into the current month</td></tr><tr><td><strong><em>@endOfYear-7d</em></strong></td><td>One week before year-end</td></tr><tr><td><strong><em>@today+10h</em></strong></td><td>10:00 today</td></tr><tr><td><strong><em>@today+10h+30m</em></strong></td><td>10:30 today</td></tr><tr><td><strong><em>@today+2d+10h+30m</em></strong></td><td>2 days from today at 10:30</td></tr><tr><td><strong><em>@now+2h</em></strong></td><td>Two hours from now</td></tr><tr><td><strong><em>@now-30m</em></strong></td><td>30 minutes ago</td></tr><tr><td><strong><em>@now+45s</em></strong></td><td>45 seconds from now</td></tr><tr><td><strong><em>@now-8h+30m</em></strong></td><td>7 hours and 30 minutes ago</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Each offset uses <strong><em>d</em></strong> for days, <strong><em>h</em></strong> for hours, <strong><em>m</em></strong> for minutes, and <strong><em>s</em></strong> for seconds. As you can see above, offsets can be chained together in any order, so <strong><em>@today+2d+10h+30m</em></strong> is valid, as is <strong><em>@now-8h+30m</em></strong>.</p>



<p class="wp-block-paragraph">The day offset is calendar days, not business days. There is no built-in concept of holidays in the engine as of now.</p>



<h3 id="h-5-3-fixed-iso-dates-with-optional-offset" class="wp-block-heading">5.3. Fixed ISO dates (with optional offset)</h3>



<p class="wp-block-paragraph">A literal ISO date works as both literal as well as a date token. However, if you want to apply an offset, then only dateToken can be used. As previously mentioned, &#8220;literal&#8221; really means a literal strings, so there is no computation done on it.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Effective through&quot;, &quot;operator&quot;: &quot;greaterOrEqual&quot;,
  &quot;value&quot;: &quot;2026-01-01+90d&quot;, &quot;valueType&quot;: &quot;dateToken&quot; }
</pre></div>


<p class="wp-block-paragraph">This resolves to <strong><em>2026-04-01 00:00:00</em></strong>, at query time. This can be useful when you want a stable absolute anchor but with an offset relative to it.</p>



<h3 id="h-5-3a-when-to-use-now-instead-of-today" class="wp-block-heading">5.3a. When to use @now instead of @today</h3>



<p class="wp-block-paragraph">The key difference between <strong><em>@now</em></strong> and <strong><em>@today</em></strong> is that <strong><em>@now</em></strong> captures the current time including hours, minutes, and seconds, while <strong><em>@today</em></strong> is truncated to midnight.</p>



<p class="wp-block-paragraph">Use <strong><em>@today</em></strong> for day-level filters (most common): &#8220;contracts expiring within 30 days&#8221;, &#8220;documents created this month&#8221;. Use <strong><em>@now</em></strong> when you need intra-day precision: &#8220;events logged in the last 2 hours&#8221;, &#8220;tasks to process in next 4 hours&#8221;. Examples:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
// All activity since this morning at 7am (a fixed time each day)
{ &quot;property&quot;: &quot;Modified&quot;, &quot;operator&quot;: &quot;greaterOrEqual&quot;,
  &quot;value&quot;: &quot;@today+7h&quot;, &quot;valueType&quot;: &quot;dateToken&quot; }

// Flagged tasks created in the last 8 hours
{ &quot;property&quot;: &quot;Created&quot;, &quot;operator&quot;: &quot;greaterOrEqual&quot;,
  &quot;value&quot;: &quot;@now-8h&quot;, &quot;valueType&quot;: &quot;dateToken&quot; }
</pre></div>


<h2 id="h-6-boolean-filter-values" class="wp-block-heading">6. Boolean filter values</h2>



<p class="wp-block-paragraph">For boolean-typed properties (M-Files &#8220;Boolean (yes/no)&#8221;), the engine accepts both <strong><em>&#8220;Yes&#8221;</em></strong> / <strong><em>&#8220;No&#8221;</em></strong> and <strong><em>&#8220;True&#8221;</em></strong> / <strong><em>&#8220;False&#8221;</em></strong> (case-insensitive):</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{ &quot;property&quot;: &quot;Accepted&quot;, &quot;operator&quot;: &quot;equals&quot;,
  &quot;value&quot;: &quot;Yes&quot; }

{ &quot;property&quot;: &quot;Accepted&quot;, &quot;operator&quot;: &quot;equals&quot;,
  &quot;value&quot;: &quot;True&quot; }
</pre></div>


<p class="wp-block-paragraph"><strong><em>&#8220;Yes&#8221;</em></strong> / <strong><em>&#8220;No&#8221;</em></strong> is the preferred form because that is what M-Files displays in the UI and what users see in their property cards. Worth noting: the M-Files Admin label &#8220;Boolean (yes/no)&#8221; is always English even on a localized vault. So you do not need <strong><em>&#8220;Ja&#8221;</em></strong> / <strong><em>&#8220;Nein&#8221;</em></strong> mappings on a German vault (at least as of today).</p>



<h2 id="h-7-wrap-up" class="wp-block-heading">7. Wrap-up</h2>



<p class="wp-block-paragraph">The query model covers the questions that come up in practice. <strong><em>objectType</em></strong> and <strong><em>class</em></strong> define the scope, <strong><em>filters</em></strong> narrow it with a rich set of operators spanning native server conditions and post-filter cases. Date tokens make filters relative without effort.</p>



<p class="wp-block-paragraph">What is <strong>not</strong> in this post: the <strong><em>aggregation</em></strong> block. That is the topic of <a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/" id="45206">Po</a><a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/" id="45206" target="_blank" rel="noreferrer noopener">st 6</a>, which covers the aggregation types, the reducers, and the multi-series <strong><em>seriesProperty</em></strong> feature. Once those are explained, every JSON block in the widget posts will make complete sense.</p>



<p class="wp-block-paragraph">Want to know more about this Business Dashboard? <a href="https://www.dbi-services.com/company/contact/" target="_blank" rel="noreferrer noopener">Contact us</a> and we will be happy to showcase it on <a href="https://www.m-files.com/" target="_blank" rel="noreferrer noopener">M-Files</a>.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/">M-Files BD &#8211; Queries: objectType, class, filters, date tokens</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>M-Files BD &#8211; Distribution and tabular widgets: donut, bar, table</title>
		<link>https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/</link>
					<comments>https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/#respond</comments>
		
		<dc:creator><![CDATA[Morgan Patou]]></dc:creator>
		<pubDate>Tue, 16 Jun 2026 17:55:41 +0000</pubDate>
				<category><![CDATA[Enterprise content management]]></category>
		<category><![CDATA[bar]]></category>
		<category><![CDATA[Business Dashboard]]></category>
		<category><![CDATA[donut]]></category>
		<category><![CDATA[M-Files]]></category>
		<category><![CDATA[table]]></category>
		<category><![CDATA[widget]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=45109</guid>

					<description><![CDATA[<p>This is the third and final widget post of the series. The previous two covered scalar widgets (Post 4a) and trend widgets (Post 4b). What is left is the distribution and tabular widgets: donut, bar, and table. The grouping is loose but useful: donut and bar answer questions like &#8220;how does my population break down [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/">M-Files BD &#8211; Distribution and tabular widgets: donut, bar, table</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">This is the third and final widget post of the series. The previous two covered scalar widgets (<a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/" target="_blank" rel="noreferrer noopener">Post 4a</a>) and trend widgets (<a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" target="_blank" rel="noreferrer noopener">Post 4b</a>). What is left is the <strong>distribution and tabular</strong> widgets: <strong><em>donut</em></strong>, <strong><em>bar</em></strong>, and <strong><em>table</em></strong>.</p>



<p class="wp-block-paragraph">The grouping is loose but useful: <strong><em>donut</em></strong> and <strong><em>bar</em></strong> answer questions like &#8220;how does my population break down into categories?&#8221;, while <strong><em>table</em></strong> can answer pretty much any question&#8230; Together, they cover the rest of the use cases, where you want either a visual distribution or the actual details of what is in the vault.</p>



<h2 id="h-1-donut-distribution-across-categories" class="wp-block-heading">1. donut &#8211; distribution across categories</h2>



<p class="wp-block-paragraph">The <strong><em>donut</em></strong> chart shows how a population splits across the distinct values of a property. Labels and values appear on the slices, while the legend is placed next to the donut when the pane is wide enough (otherwise below it). The engine computes a layout that estimates the legend width from the longest label, then centers the donut in the remaining space, with the ring filling the main part of the available area.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{
  &quot;id&quot;: &quot;&quot;,
  &quot;title&quot;: &quot;Contracts by Agreement Type&quot;,
  &quot;type&quot;: &quot;donut&quot;,
  &quot;gridColumnSpan&quot;: 6,
  &quot;gridRowSpan&quot;: 2,
  &quot;query&quot;: {
    &quot;objectType&quot;: &quot;Document&quot;,
    &quot;class&quot;: &quot;Contract or Agreement&quot;,
    &quot;aggregation&quot;: {
      &quot;type&quot;: &quot;groupByProperty&quot;,
      &quot;propertyName&quot;: &quot;Agreement type&quot;,
      &quot;includeEmptyResults&quot;: &quot;No&quot;
    }
  }
}
</pre></div>


<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1216c88b&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1216c88b" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="836" height="472" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.1.0-1.png" alt="Distribution and tabular widget - Donut" class="wp-image-45138" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.1.0-1.png 836w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.1.0-1-300x169.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.1.0-1-768x434.png 768w" sizes="auto, (max-width: 836px) 100vw, 836px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h3 id="h-1-1-aggregations-the-donut-accepts" class="wp-block-heading">1.1. Aggregations the donut accepts</h3>



<p class="wp-block-paragraph">Three aggregation types work with <strong><em>donut</em></strong>:</p>



<ul class="wp-block-list">
<li><strong><em>groupByProperty</em></strong>: group by the values of that specific property, often the natural choice for category distributions.</li>



<li><strong><em>groupByDateBucket</em></strong>: group by dates based on a certain bucket size. The date buckets will be ordered chronologically, where usually donut slices would be ordered by decreasing counts of objects.</li>



<li><strong><em>groupByRange</em></strong>: group by manually defined range of things (string, number), often the natural choice for large set of values (e.g. revenues)</li>
</ul>



<h3 id="h-1-2-resolvevaluelistlabels" class="wp-block-heading">1.2. resolveValueListLabels</h3>



<p class="wp-block-paragraph">For properties backed by an M-Files value list, <strong><em>resolveValueListLabels: &#8220;Yes&#8221;</em></strong> (the default) shows the human display names on the slices&#8217; tooltip and in the legend. On the other hand, <strong><em>&#8220;No&#8221;</em></strong> shows the internal IDs. The default is correct for almost every dashboard, <strong><em>&#8220;No&#8221;</em></strong> might be useful for technical investigations. That&#8217;s why this field isn&#8217;t show in the Visual Designer, to avoid issues. If you want to set it, you will need to do it from the JSON editor directly.</p>



<h3 id="h-1-3-seriesproperty-the-multi-mini-pie-grid" class="wp-block-heading">1.3. seriesProperty: the multi-mini-pie grid</h3>



<p class="wp-block-paragraph">This is the most distinctive feature of the donut widget. When you set a <strong><em>seriesProperty</em></strong>, the widget switches to a <strong>multi-mini-pie grid</strong>: one donut per distinct value of the series property, all sharing a single legend. Each small donut shows the distribution of the primary <strong><em>propertyName</em></strong> within that series slice.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{
  &quot;id&quot;: &quot;&quot;,
  &quot;title&quot;: &quot;Contracts by Agreement Type x Year&quot;,
  &quot;type&quot;: &quot;donut&quot;,
  &quot;gridColumnSpan&quot;: 12,
  &quot;gridRowSpan&quot;: 4,
  &quot;query&quot;: {
    &quot;objectType&quot;: &quot;Document&quot;,
    &quot;class&quot;: &quot;Contract or Agreement&quot;,
    &quot;aggregation&quot;: {
      &quot;type&quot;: &quot;groupByDateBucket&quot;,
      &quot;propertyName&quot;: &quot;Effective through&quot;,
      &quot;bucketSize&quot;: &quot;year&quot;,
      &quot;seriesProperty&quot;: &quot;Agreement type&quot;,
      &quot;includeEmptyResults&quot;: &quot;No&quot;
    }
  }
}
</pre></div>


<p class="wp-block-paragraph">This produces, for example, one donut per Agreement type, with each donut showing the distribution by year for their Effective through property. As you can see below, it takes more space, since multiple donuts will be displayed. Depending on what you want to see, you could also simply display multiple donut widgets with the associated filters, instead of a single widget with a multi-series. That is, if you know how many series you need (and therefore how many widgets you need with their dedicated filters) and if it&#8217;s not a dynamic number.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1216d0f7&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1216d0f7" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1246" height="926" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.1.3-1.png" alt="M-Files distribution chart - multi-series donuts" class="wp-image-45140" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.1.3-1.png 1246w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.1.3-1-300x223.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.1.3-1-1024x761.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.1.3-1-768x571.png 768w" sizes="auto, (max-width: 1246px) 100vw, 1246px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<p class="wp-block-paragraph">A few practical notes:</p>



<ul class="wp-block-list">
<li>The donuts are all <strong>resize-aware</strong>: when the right pane is resized, the engine recomputes the size of all donuts. The number of donuts per row is fixed and defined at first rendering, based on the size that the widget has available (based on its configuration) and the number of series that were found.</li>



<li>A series where every visible slice is zero is shown as a <strong>grey ring</strong>, so the user does not have to wonder whether the chart loaded. This is the same for single-series donuts too.</li>



<li>Like every <strong><em>seriesProperty</em></strong>, this should be a <strong>low-cardinality</strong> property. A grid of six donuts is most probably fine, but a grid of fifty might be difficult to display properly, unless if you have a very big screen!</li>
</ul>



<h3 id="h-1-4-the-tooltip-touch" class="wp-block-heading">1.4. The tooltip touch</h3>



<p class="wp-block-paragraph">As all other tooltips, the one on the Donut will show the numbers that match the query with a thousand separator (e.g. <strong><em>1&#8217;520</em></strong> or <strong><em>1,520</em></strong> (depending on your regional settings) rather than <strong><em>1520</em></strong>). But in addition, it will also include the percentage that this slice represents. It is a small detail, but it usually helps and avoids the need to calculate that yourself.</p>



<h2 id="h-2-bar-vertical-and-horizontal-bar-charts" class="wp-block-heading">2. bar &#8211; vertical and horizontal bar charts</h2>



<p class="wp-block-paragraph">The <strong><em>bar</em></strong> widget covers a large surface and deserves its own deep dive. It supports both vertical and horizontal orientations, both single-series and multi-series modes, and per-bar threshold coloring in single-series too. Most of the configuration lives in two <strong><em>display</em></strong> keys: <strong><em>barLayout</em></strong> and <strong><em>thresholds</em></strong>.</p>



<h3 id="h-2-1-a-first-vertical-bar" class="wp-block-heading">2.1. A first vertical bar</h3>



<p class="wp-block-paragraph">Taking the same example as for the <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" id="45076" target="_blank" rel="noreferrer noopener">previous blog post</a> on the Invoices/Revenue:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{
  &quot;id&quot;: &quot;&quot;,
  &quot;title&quot;: &quot;Monthly revenue&quot;,
  &quot;type&quot;: &quot;bar&quot;,
  &quot;gridColumnSpan&quot;: 12,
  &quot;gridRowSpan&quot;: 2,
  &quot;query&quot;: {
    &quot;objectType&quot;: &quot;Document&quot;,
    &quot;class&quot;: &quot;Invoice&quot;,
    &quot;filters&quot;: &#x5B;
      { &quot;property&quot;: &quot;Invoice date&quot;, &quot;operator&quot;: &quot;between&quot;,
        &quot;value&quot;: &#x5B;&quot;@startOfYear&quot;, &quot;@endOfYear&quot;], &quot;valueType&quot;: &quot;dateToken&quot; }
    ],
    &quot;aggregation&quot;: {
      &quot;type&quot;: &quot;groupByDateBucket&quot;,
      &quot;propertyName&quot;: &quot;Invoice date&quot;,
      &quot;bucketSize&quot;: &quot;month&quot;,
      &quot;reducer&quot;: &quot;sum&quot;,
      &quot;reducerProperty&quot;: &quot;Amount&quot;,
      &quot;includeEmptyResults&quot;: &quot;Yes&quot;
    }
  }
}
</pre></div>


<p class="wp-block-paragraph">The X-axis labels rotate at <strong><em>-15 degrees</em></strong> always (not conditionally), so they never overlap the bars or the chart area below them. This was a small thing needed to avoid strange display when the space for the widget is very small or when there are long labels to be displayed. As you can see below, it is very similar to the line/area widgets in its essence:</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1216d842&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1216d842" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1658" height="472" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.1.png" alt="Distribution and tabular widget - table" class="wp-image-45112" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.1.png 1658w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.1-300x85.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.1-1024x292.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.1-768x219.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.1-1536x437.png 1536w" sizes="auto, (max-width: 1658px) 100vw, 1658px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h3 id="h-2-2-display-thresholds-in-single-series-mode" class="wp-block-heading">2.2. display.thresholds in single-series mode</h3>



<p class="wp-block-paragraph">In single-series mode, <strong><em>display.thresholds</em></strong> colors <strong>each bar individually</strong> based on its own value. You can either use text values representing colors or the HTML color code:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;display&quot;: {
  &quot;thresholds&quot;: &#x5B;
    { &quot;value&quot;: 0, &quot;color&quot;: &quot;red&quot; },
    { &quot;value&quot;: 40000, &quot;color&quot;: &quot;orange&quot; },
    { &quot;value&quot;: 100000, &quot;color&quot;: &quot;yellow&quot; },
    { &quot;value&quot;: 120000, &quot;color&quot;: &quot;green&quot; }
  ]
}
</pre></div>


<p class="wp-block-paragraph">So bars with value 0 to 40&#8217;000 will be displayed in red (when value is 0, there is no bar, so only the tooltip will show a red &#8220;0&#8221;). Bars with 40&#8217;000 to 100&#8217;000 will become orange, etc.:</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1216de03&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1216de03" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1658" height="472" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.2.png" alt="M-Files distribution chart - table with threshold colors" class="wp-image-45113" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.2.png 1658w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.2-300x85.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.2-1024x292.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.2-768x219.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.2-1536x437.png 1536w" sizes="auto, (max-width: 1658px) 100vw, 1658px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<p class="wp-block-paragraph">As with <strong><em>line</em></strong> / <strong><em>area</em></strong>, thresholds are <strong>ignored in multi-series mode</strong> (each series uses the automatic palette so the legend color stays consistent).</p>



<h3 id="h-2-3-display-barlayout" class="wp-block-heading">2.3. display.barLayout</h3>



<p class="wp-block-paragraph">Bar charts have two orthogonal choices: orientation (vertical vs horizontal) and multi-series mode (stacked vs grouped). The <strong><em>display.barLayout</em></strong> key combines them into a single value:</p>



<figure class="wp-block-table"><table><thead><tr><th>Value</th><th>Orientation</th><th>Multi-series mode</th></tr></thead><tbody><tr><td><strong><em>&#8220;vertical-stacked&#8221;</em></strong> <em>(default)</em></td><td>Vertical</td><td>Stacked segments</td></tr><tr><td><strong><em>&#8220;vertical-grouped&#8221;</em></strong></td><td>Vertical</td><td>Side-by-side bars per category</td></tr><tr><td><strong><em>&#8220;horizontal-stacked&#8221;</em></strong></td><td>Horizontal</td><td>Stacked segments</td></tr><tr><td><strong><em>&#8220;horizontal-grouped&#8221;</em></strong></td><td>Horizontal</td><td>Side-by-side bars per category</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">For <strong>single-series</strong> bars (i.e. when no <strong><em>seriesProperty</em></strong> is used), only orientation matters. The stacked / grouped distinction has no visible effect, since there is a single value anyway). So a single-series chart with <strong><em>vertical-stacked</em></strong> and one with <strong><em>vertical-grouped</em></strong> render identically.</p>



<p class="wp-block-paragraph">Horizontal charts place categories on the Y axis (top to bottom in the same order they would appear left to right on a vertical chart) and values on the X axis. This is useful when category labels are long enough that the -15 degree rotation still does not fit or when you want more space to display the values rather than the different categories.</p>



<p class="wp-block-paragraph">See the screenshot on the following section for the distinction between the vertical/horizontal and stacked/grouped.</p>



<h3 id="h-2-4-seriesproperty-stacked-or-grouped" class="wp-block-heading">2.4. seriesProperty: stacked or grouped</h3>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByDateBucket&quot;,
  &quot;propertyName&quot;: &quot;Invoice date&quot;,
  &quot;bucketSize&quot;: &quot;month&quot;,
  &quot;reducer&quot;: &quot;sum&quot;,
  &quot;reducerProperty&quot;: &quot;Amount&quot;,
  &quot;seriesProperty&quot;: &quot;Country&quot;,
  &quot;includeEmptyResults&quot;: &quot;Yes&quot;
}

</pre></div>


<p class="wp-block-paragraph">With <strong><em>seriesProperty: &#8220;Country&#8221;</em></strong> and <strong><em>display.barLayout: &#8220;vertical-stacked&#8221;</em></strong>, you get a stacked bar chart where each month bar is split into each values found for the &#8220;Country&#8221; property (here: Switzerland &amp; USA). With <strong><em>vertical-grouped</em></strong>, you get two side-by-side bars per month (one for each &#8220;Country&#8221; value). The chart tells the same story with two (or rather four) different displays. Note: for the below screenshot, I reduced the filtering to only the first 6 months of the year, to be able to do a side-by-side comparison of how these charts look. The goal isn&#8217;t to see the full year here, only to show you how these different display options render:</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1216e4a4&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1216e4a4" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1658" height="934" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.4.png" alt="M-Files distribution chart - bar with multi-series" class="wp-image-45114" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.4.png 1658w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.4-300x169.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.4-1024x577.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.4-768x433.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.2.4-1536x865.png 1536w" sizes="auto, (max-width: 1658px) 100vw, 1658px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h3 id="h-2-5-aggregations-the-bar-widget-accepts" class="wp-block-heading">2.5. Aggregations the bar widget accepts</h3>



<p class="wp-block-paragraph">The bar widget accepts <strong><em>groupByProperty</em></strong>, <strong><em>groupByDateBucket</em></strong> and <strong><em>groupByRange</em></strong>, same as the donut and trend widgets, and the date-valued reducer rule from <a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/" id="45018" target="_blank" rel="noreferrer noopener">posts 4a</a> and <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" id="45076" target="_blank" rel="noreferrer noopener">4b</a> applies here too: a reducer that returns a date string cannot be plotted on a numeric axis, so the widget will show a placeholder.</p>



<h2 id="h-3-table-sortable-paginated-multipurpose" class="wp-block-heading">3. table &#8211; sortable, paginated, multipurpose</h2>



<p class="wp-block-paragraph">The <strong><em>table</em></strong> widget is the last, and most flexible of the catalog. It accepts <strong>all five aggregation types</strong> (<strong><em>summary</em></strong>, <strong><em>groupByProperty</em></strong>, <strong><em>groupByDateBucket</em></strong>, <strong><em>groupByRange</em></strong>, <strong><em>list</em></strong>), and it is the <strong>only</strong> widget that supports <strong><em>list</em></strong>. This makes it the natural fallback when other widget types cannot display a specific combination, and the obvious choice when the user wants to see the raw objects (without the drill-through).</p>



<h3 id="h-3-1-a-list-aggregation-table" class="wp-block-heading">3.1. A list-aggregation table</h3>



<p class="wp-block-paragraph">This is the &#8220;give me the details&#8221; case. The aggregation is <strong><em>list</em></strong> and with the <strong><em>displayProperties</em></strong> parameter, it is possible to control which columns are shown alongside the object name.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{
  &quot;id&quot;: &quot;&quot;,
  &quot;title&quot;: &quot;All Projects&quot;,
  &quot;type&quot;: &quot;table&quot;,
  &quot;gridColumnSpan&quot;: 12,
  &quot;gridRowSpan&quot;: 4,
  &quot;query&quot;: {
    &quot;objectType&quot;: &quot;Project&quot;,
    &quot;aggregation&quot;: {
      &quot;type&quot;: &quot;list&quot;,
      &quot;displayProperties&quot;: &#x5B;&quot;Class&quot;, &quot;In progress&quot;, &quot;Customer&quot;, &quot;Project Manager&quot;]
    }
  }
}
</pre></div>


<p class="wp-block-paragraph">The result is a sortable table with one row per <strong><em>Project</em></strong>, and five columns (object name, Class, In progress, Customer and Project Manager). Above, there was no Class enforced, so all classes from M-Files will apply. That&#8217;s why in the screenshot, you can see both <strong><em>Internal Project</em></strong> and <strong><em>Customer Project</em></strong> objects, which are the two classes in question. Only the <strong><em>Customer Project</em></strong> has a <strong><em>Customer</em></strong> property, but it isn&#8217;t a problem, it is simply displayed as <strong><em>&#8220;-&#8220;</em></strong> for the <strong><em>Internal Project</em></strong> objects. Clicking a row name navigates the user directly to the object in M-Files, no drill-through modal is opening, since the row already represents one object.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1216eb80&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1216eb80" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="2044" height="934" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.1.png" alt="Distribution and tabular widget - table" class="wp-image-45115" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.1.png 2044w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.1-300x137.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.1-1024x468.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.1-768x351.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.1-1536x702.png 1536w" sizes="auto, (max-width: 2044px) 100vw, 2044px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h3 id="h-3-2-sorting-and-pagination" class="wp-block-heading">3.2. Sorting and pagination</h3>



<p class="wp-block-paragraph">Every column header is clickable to sort ascending or descending. In above screenshot, the second column is sorted in descending order. The sort applies across <strong>all fetched rows</strong>, not just the currently visible page, which (I believe) is the behavior users would expect when sorting.</p>



<p class="wp-block-paragraph"><strong><em>Note:</em></strong> This is NOT how M-Files behaves in its own search! In M-Files, by default, when you search for something and you get more than 1 page of results, if you sort a column, only what is displayed on the screen gets sorted&#8230; Which is a problem from my point of view, since it sorts only partial data and you might see things you don&#8217;t expect. Therefore, M-Files is doing a page-by-page sorting and not a global sorting. On the other hand, the <strong><em>Business Dashboard</em></strong> will sort correctly and apply the sorting on ALL data, and not just the current page.</p>



<p class="wp-block-paragraph">Pagination kicks in at 15 rows per page, by default (configurable), and the pagination bar at the bottom shows the current range and the total fetched.</p>



<h3 id="h-3-3-display-details" class="wp-block-heading">3.3. Display details</h3>



<p class="wp-block-paragraph">Numeric columns honor <strong><em>display.decimals</em></strong> and both numeric and dates honors regional settings as well. In above example, you can see the 4th row with a <strong><em>Customer</em></strong> column that shows a value <strong><em>&#8220;Tennessee Land Surveyors (Nashville) || Lance Smith Engineering (Surveying)&#8221;</em></strong>. This is actually a concatenation of two customers. This project, in the M-Files Sample Vault, is assigned to two different customers, and therefore, it is shown in the table (or any drill-through modal) as a concatenation of <strong><em>Customer1 || Customer2</em></strong>. No data is silently cut or hidden, everything that is actually part of the metadata model is shown as it should. That&#8217;s how multi-select lookup values are displayed.</p>



<h3 id="h-3-4-seriesproperty-the-cross-tab-pivot" class="wp-block-heading">3.4. seriesProperty: the cross-tab pivot</h3>



<p class="wp-block-paragraph">As other widgets, the table supports multi-series (via the same <strong><em>seriesProperty</em></strong>) when it is using a <strong><em>groupByProperty</em></strong>, <strong><em>groupByDateBucket</em></strong> or <strong><em>groupByRange</em></strong> aggregation. In this case, it becomes a <strong>cross-tab pivot</strong>. This means that rows will remain the groups for the primary group property, while additional columns will be added for each group of the series property. Therefore, both rows (primary group) and columns (series group) are fully dynamic. Here are two table widgets definitions, the first one without multi-series and the second one with it:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByProperty&quot;,
  &quot;propertyName&quot;: &quot;Project manager&quot;,
  &quot;displayProperties&quot;: &#x5B;&quot;Class&quot;, &quot;In progress&quot;, &quot;Customer&quot;, &quot;Project Manager&quot;]
}
...
...
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByProperty&quot;,
  &quot;propertyName&quot;: &quot;Project manager&quot;,
  &quot;seriesProperty&quot;: &quot;Class&quot;,
  &quot;displayProperties&quot;: &#x5B;&quot;Class&quot;, &quot;In progress&quot;, &quot;Customer&quot;, &quot;Project Manager&quot;]
}
</pre></div>


<p class="wp-block-paragraph">With the above, you get the count of <strong><em>Projects</em></strong> that each <strong><em>Project Manager</em></strong> handles for the first one. While the second widget, adds another dimension that is the <strong><em>&#8220;Class&#8221;</em></strong> of the objects, i.e. whether the <strong><em>Projects</em></strong> that the managers handle is an <strong><em>Internal Project</em></strong> or a <strong><em>Customer Project</em></strong>. The result of these two widgets is the following:</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1216f268&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1216f268" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1658" height="934" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.4.png" alt="M-Files distribution chart - table with multi-series" class="wp-image-45116" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.4.png 1658w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.4-300x169.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.4-1024x577.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.4-768x433.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4c.3.4-1536x865.png 1536w" sizes="auto, (max-width: 1658px) 100vw, 1658px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<p class="wp-block-paragraph">In this multi-series case, all series columns include a link on their count. Therefore, each number is clickable, and it will display the drill-through that matches this series AND this primary group. E.g. which <strong><em>Customer Projects</em></strong> are handled by <strong><em>Tommy Hart</em></strong>.</p>



<h3 id="h-3-5-drill-through-on-tables" class="wp-block-heading">3.5. Drill-through on tables</h3>



<p class="wp-block-paragraph">Table drill-through behavior depends on the aggregation type, which is documented in detail in <a href="https://www.dbi-services.com/blog/m-files-bd-end-user-experience/" target="_blank" rel="noreferrer noopener">Post 2</a> (the end-user post) but worth recapping here:</p>



<ul class="wp-block-list">
<li>For <strong><em>summary</em></strong>, <strong><em>groupByProperty</em></strong>, <strong><em>groupByDateBucket</em></strong> or <strong><em>groupByRange</em></strong>: clicking a row (or a specific column from a row in case of multi-series as mentioned above) opens the drill-through modal showing the underlying objects.</li>



<li>For <strong><em>list</em></strong>: clicking a row name navigates directly to the object. No modal, because the row already represents one object.</li>
</ul>



<h3 id="h-3-6-the-safe-fallback-role" class="wp-block-heading">3.6. The &#8220;safe fallback&#8221; role</h3>



<p class="wp-block-paragraph">I mentioned earlier that <strong><em>table</em></strong> accepts every aggregation type. This makes it the safe choice for cases where other widgets refuse. The most common one is <strong>date-valued reducers</strong>: if the answer to your question is a date per group (e.g. &#8220;latest invoice date per customer&#8221;), the chart widgets cannot plot that, but the <strong><em>table</em></strong> widget will happily render it with one row per customer and a date value for each row.</p>



<p class="wp-block-paragraph">Whenever the validation flags a date-valued reducer warning on a chart widget, switching the widget type to <strong><em>kpiNumber</em></strong> or <strong><em>gauge</em></strong> might be the right answer if you need only one date. Otherwise, switching to <strong><em>table</em></strong> is the only remaining and valid option.</p>



<h2 id="h-4-the-compatibility-cheat-sheet" class="wp-block-heading">4. The compatibility cheat sheet</h2>



<p class="wp-block-paragraph">To close the widget posts, here is the full compatibility matrix the validator enforces, as of now. You can use that as a quick reference when authoring:</p>



<figure class="wp-block-table"><table><thead><tr><th class="has-text-align-center" data-align="center">Widget type</th><th class="has-text-align-center" data-align="center">summary</th><th class="has-text-align-center" data-align="center">groupByProperty</th><th class="has-text-align-center" data-align="center">groupByDateBucket</th><th class="has-text-align-center" data-align="center">groupByRange</th><th class="has-text-align-center" data-align="center">list</th><th class="has-text-align-center" data-align="center">seriesProperty</th></tr></thead><tbody><tr><td class="has-text-align-center" data-align="center"><strong><em>kpiNumber</em></strong></td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">ignored</td></tr><tr><td class="has-text-align-center" data-align="center"><strong><em>gauge</em></strong></td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">ignored</td></tr><tr><td class="has-text-align-center" data-align="center"><strong><em>donut</em></strong></td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">multi-mini-pie</td></tr><tr><td class="has-text-align-center" data-align="center"><strong><em>bar</em></strong></td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">stacked or grouped</td></tr><tr><td class="has-text-align-center" data-align="center"><strong><em>line</em></strong></td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">multi-series lines</td></tr><tr><td class="has-text-align-center" data-align="center"><strong><em>area</em></strong></td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">&#8211;</td><td class="has-text-align-center" data-align="center">multi-series areas</td></tr><tr><td class="has-text-align-center" data-align="center"><strong><em>table</em></strong></td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">yes</td><td class="has-text-align-center" data-align="center">cross-tab pivot</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">When the validation reports an incompatible combination, the message usually includes the right alternative (typically &#8220;use <strong><em>kpiNumber</em></strong>, <strong><em>gauge</em></strong> or <strong><em>table</em></strong>&#8221; for date-valued reducers, or &#8220;use <strong><em>table</em></strong> for list aggregations&#8221;).</p>



<h2 id="h-5-closing-thoughts" class="wp-block-heading">5. Closing thoughts</h2>



<p class="wp-block-paragraph">That closes the widget catalog. The seven widget types cover the full range of business questions I typically see: scalar answers (<strong><em>kpiNumber</em></strong>, <strong><em>gauge</em></strong>), trends (<strong><em>line</em></strong>, <strong><em>area</em></strong>), distributions (<strong><em>donut</em></strong>, <strong><em>bar</em></strong>), and raw enumerations or whatever else (<strong><em>table</em></strong>).</p>



<p class="wp-block-paragraph">Two observations from real customer dashboards:</p>



<ul class="wp-block-list">
<li><strong>Most useful dashboards mix widget types.</strong> A row of KPIs at the top, a couple of distribution charts in the middle, a list table at the bottom. The mental model the user builds reading top to bottom is &#8220;summary, distribution, details&#8221;.</li>



<li><strong>Most pitfalls live at the boundary between aggregation and widget type.</strong> If a widget shows a placeholder, the explanation is almost always &#8220;this aggregation produces a shape this widget cannot render&#8221;. The validation spells it out before save, so trust it.</li>
</ul>



<p class="wp-block-paragraph"><a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/" id="45175" target="_blank" rel="noreferrer noopener">Post 5</a> shifts from &#8220;what widgets exist&#8221; to &#8220;how to write the query that feeds them&#8221;: object types, classes, filter operators, and date tokens. After that, <a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/" id="45206" target="_blank" rel="noreferrer noopener">Post 6</a> covers aggregations and reducers in detail. With those two, every JSON block in the widget posts will make complete sense.</p>



<p class="wp-block-paragraph">Want to know more about this Business Dashboard? <a href="https://www.dbi-services.com/company/contact/" target="_blank" rel="noreferrer noopener">Contact us</a> and we will be happy to showcase it on <a href="https://www.m-files.com/" target="_blank" rel="noreferrer noopener">M-Files</a>.</p>



<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/">M-Files BD &#8211; Distribution and tabular widgets: donut, bar, table</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>M-Files BD &#8211; Trend widgets: line and area</title>
		<link>https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/</link>
					<comments>https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/#respond</comments>
		
		<dc:creator><![CDATA[Morgan Patou]]></dc:creator>
		<pubDate>Sat, 13 Jun 2026 16:34:04 +0000</pubDate>
				<category><![CDATA[Enterprise content management]]></category>
		<category><![CDATA[area]]></category>
		<category><![CDATA[Business Dashboard]]></category>
		<category><![CDATA[line]]></category>
		<category><![CDATA[M-Files]]></category>
		<category><![CDATA[widget]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=45076</guid>

					<description><![CDATA[<p>This is the second widget post of the series, focused on the two trend widgets: line and area. The previous one covered scalar widgets (Post 4a). Since these two widgets are closely related, it made sense to bundle them together in the same post. Both surface a single value per category or per time bucket, [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/">M-Files BD &#8211; Trend widgets: line and area</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">This is the second widget post of the series, focused on the two trend widgets: <strong><em>line</em></strong> and <strong><em>area</em></strong>. The previous one covered scalar widgets (<a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/" id="45018" target="_blank" rel="noreferrer noopener">Post 4a</a>). Since these two widgets are closely related, it made sense to bundle them together in the same post. Both surface a single value per category or per time bucket, plotted across a horizontal axis. They are best used with the <strong><em>groupByDateBucket</em></strong> aggregation to show evolution over time, but they also accept <strong><em>groupByProperty</em></strong> and <strong><em>groupByRange</em></strong>.</p>



<h2 id="h-1-why-these-two-are-in-the-same-post" class="wp-block-heading">1. Why these two are in the same post</h2>



<p class="wp-block-paragraph">The wire format and the rendering pipeline for <strong><em>line</em></strong> and <strong><em>area</em></strong> are nearly identical. They produce the same JSON on the server, they use the same client code path, they accept the same aggregation types, the same reducers, the same <strong><em>display</em></strong> options. The only meaningful difference is visual: <strong><em>area</em></strong> fills the region under the line with a translucent color, while <strong><em>line</em></strong> leaves it transparent.</p>



<p class="wp-block-paragraph">In other words: choose <strong><em>area</em></strong> when you want to emphasize cumulative volume or magnitude, choose <strong><em>line</em></strong> when you want to emphasize the trajectory. Both communicate the same thing, it&#8217;s more about what you want to focus on.</p>



<h2 id="h-2-a-first-example-monthly-revenue-trend" class="wp-block-heading">2. A first example: monthly revenue trend</h2>



<p class="wp-block-paragraph">Here is the canonical example: total invoice amount per month for the ongoing year, displayed as a line chart.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{
  &quot;id&quot;: &quot;&quot;,
  &quot;title&quot;: &quot;Monthly revenue&quot;,
  &quot;type&quot;: &quot;line&quot;,
  &quot;gridColumnSpan&quot;: 12,
  &quot;gridRowSpan&quot;: 2,
  &quot;display&quot;: { &quot;smooth&quot;: &quot;Yes&quot;, &quot;decimals&quot;: 0 },
  &quot;query&quot;: {
    &quot;objectType&quot;: &quot;Document&quot;,
    &quot;class&quot;: &quot;Invoice&quot;,
    &quot;filters&quot;: &#x5B;
      { &quot;property&quot;: &quot;Invoice date&quot;, &quot;operator&quot;: &quot;between&quot;,
        &quot;value&quot;: &#x5B;&quot;@startOfYear&quot;, &quot;@endOfYear&quot;], &quot;valueType&quot;: &quot;dateToken&quot; }
    ],
    &quot;aggregation&quot;: {
      &quot;type&quot;: &quot;groupByDateBucket&quot;,
      &quot;propertyName&quot;: &quot;Invoice date&quot;,
      &quot;bucketSize&quot;: &quot;month&quot;,
      &quot;reducer&quot;: &quot;sum&quot;,
      &quot;reducerProperty&quot;: &quot;Amount&quot;,
      &quot;includeEmptyResults&quot;: &quot;Yes&quot;
    }
  }
}
</pre></div>


<p class="wp-block-paragraph">A few things to note in this JSON:</p>



<ul class="wp-block-list">
<li>The filter restricts the data to the current calendar year using two date tokens. The full date-token vocabulary is covered in the future <a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/" id="45175" target="_blank" rel="noreferrer noopener">Post 5</a>.</li>



<li>The aggregation is <strong><em>groupByDateBucket</em></strong> on the same date property, bucketed by <strong><em>month</em></strong>. Each bucket is reduced with <strong><em>sum</em></strong> of the <strong><em>Amount</em></strong> property. In other words, this means that all invoices with a date within this calendar year will be grouped by month and the value coming from the Amount property will all be summed within each month to arrive to a Total per month.</li>



<li><strong><em>includeEmptyResults: &#8220;Yes&#8221;</em></strong> ensures that months with no invoices appear as zero values, so the trend line is continuous from January to December rather than skipping empty months (e.g. you were closed for vacations or weren&#8217;t able to sell anything for some other reasons).</li>
</ul>



<p class="wp-block-paragraph">Switching the widget <strong><em>type</em></strong> from <strong><em>line</em></strong> to <strong><em>area</em></strong> in this exact JSON gives the area-chart variant, nothing else changes:</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1217cf46&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1217cf46" class="wp-block-image size-full is-style-default wp-lightbox-container"><img loading="lazy" decoding="async" width="1658" height="936" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.2-1.png" alt="Line and area widgets" class="wp-image-45084" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.2-1.png 1658w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.2-1-300x169.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.2-1-1024x578.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.2-1-768x434.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.2-1-1536x867.png 1536w" sizes="auto, (max-width: 1658px) 100vw, 1658px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-3-the-aggregation-types-these-widgets-accept" class="wp-block-heading">3. The aggregation types these widgets accept</h2>



<p class="wp-block-paragraph"><strong><em>line</em></strong> and <strong><em>area</em></strong> are valid with three aggregation types:</p>



<ul class="wp-block-list">
<li><strong><em>groupByDateBucket</em></strong>: the natural fit for time series. It groups a date / timestamp property into a certain bucket: <strong><em>day</em></strong>, <strong><em>week</em></strong>, <strong><em>month</em></strong>, <strong><em>quarter</em></strong>, or <strong><em>year</em></strong>.</li>



<li><strong><em>groupByProperty</em></strong>: groups by the distinct values of any property. It supports date/time properties, but it&#8217;s uncommon, as each day would be a data-point. It also support any other type of properties, to display them as categorical trends (e.g. &#8220;Monthly revenue per region&#8221; rendered as a line for some reason). Depending on requirements, a <strong><em>bar</em></strong> display might be more adapted.</li>



<li><strong><em>groupByRange</em></strong>: define manual ranges of something (string or number) and turn them into points to plot (e.g. Revenue &lt;1k, &lt;10k, &lt;50k, &lt;100k, &gt;=100k)</li>
</ul>



<p class="wp-block-paragraph">The visual designer will only allow you to select one of these three aggregations. But if you try to set a <strong><em>summary</em></strong> or <strong><em>list</em></strong> aggregation in the JSON editor directly, then an error will be thrown, as it isn&#8217;t possible.</p>



<h2 id="h-4-display-smooth" class="wp-block-heading">4. display.smooth</h2>



<p class="wp-block-paragraph">The single most common <strong><em>display</em></strong> option for these widgets is <strong><em>smooth</em></strong>:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;display&quot;: { &quot;smooth&quot;: &quot;Yes&quot; }
</pre></div>


<p class="wp-block-paragraph">When set to <strong><em>&#8220;Yes&#8221;</em></strong>, the line/area charts draws a Bézier-smoothed curve between data points (c.f. example above). When <strong><em>&#8220;No&#8221;</em></strong> (or omitted), the connecting line is made of straight segments. Smoothing makes monthly or quarterly trends look more visually pleasant. It can also slightly hide outliers, so use it where the trend story is more important than the exact per-bucket value.</p>



<h2 id="h-5-display-decimals" class="wp-block-heading">5. display.decimals</h2>



<p class="wp-block-paragraph">For numeric reducers (<strong><em>sum</em></strong>, <strong><em>avg</em></strong>, <strong><em>min</em></strong>, <strong><em>max</em></strong>, <strong><em>median</em></strong>), this controls how many decimal places are shown in tooltips (default <strong><em>0</em></strong>).</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;display&quot;: { &quot;smooth&quot;: &quot;Yes&quot;, &quot;decimals&quot;: 2 }
</pre></div>


<p class="wp-block-paragraph">In the monthly-revenue example above, if amounts are stored as floats (which is probably the case), you may want <strong><em>decimals: 2</em></strong> so the tooltip would show <strong><em>116&#8217;520.43</em></strong> rather than <strong><em>116&#8217;520</em></strong>.</p>



<h2 id="h-6-display-thresholds-in-single-series-mode" class="wp-block-heading">6. display.thresholds in single-series mode</h2>



<p class="wp-block-paragraph">In single-series mode (no <strong><em>seriesProperty</em></strong>), the <strong><em>thresholds</em></strong> array colors <strong>each data point marker</strong> based on its own value. The connecting line (and the area below) itself stays brand blue. The configuration is the same as on <strong><em>kpiNumber</em></strong> and <strong><em>gauge</em></strong>. You can either use text values representing colors or the HTML color code:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;display&quot;: {
  &quot;smooth&quot;: &quot;Yes&quot;,
  &quot;thresholds&quot;: &#x5B;
    { &quot;value&quot;: 0, &quot;color&quot;: &quot;red&quot; },
    { &quot;value&quot;: 40000, &quot;color&quot;: &quot;orange&quot; },
    { &quot;value&quot;: 100000, &quot;color&quot;: &quot;yellow&quot; },
    { &quot;value&quot;: 120000, &quot;color&quot;: &quot;green&quot; }
  ]
}
</pre></div>


<p class="wp-block-paragraph">This is useful to highlight specific buckets that crossed a business threshold without changing the overall trend visualization. As mentioned in <a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/">Post 4a</a>, the default falls back to brand blue for values below the lowest threshold.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1217d7c6&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1217d7c6" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1658" height="936" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.6-1.png" alt="Line and area widgets with thresholds" class="wp-image-45087" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.6-1.png 1658w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.6-1-300x169.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.6-1-1024x578.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.6-1-768x434.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.6-1-1536x867.png 1536w" sizes="auto, (max-width: 1658px) 100vw, 1658px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-7-seriesproperty-multiple-lines-on-the-same-chart" class="wp-block-heading">7. seriesProperty: multiple lines on the same chart</h2>



<p class="wp-block-paragraph">This is where these widgets become genuinely powerful. When you set a <strong><em>seriesProperty</em></strong> on the aggregation, the engine produces <strong>one line per distinct value</strong> of that additional property, with an automatic legend and color palette.</p>



<p class="wp-block-paragraph">For instance, splitting the monthly revenue line into separate lines per country:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByDateBucket&quot;,
  &quot;propertyName&quot;: &quot;Invoice date&quot;,
  &quot;bucketSize&quot;: &quot;month&quot;,
  &quot;reducer&quot;: &quot;sum&quot;,
  &quot;reducerProperty&quot;: &quot;Amount&quot;,
  &quot;seriesProperty&quot;: &quot;Country&quot;,
  &quot;includeEmptyResults&quot;: &quot;Yes&quot;
}
</pre></div>


<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1217dcad&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1217dcad" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1656" height="1392" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.7-1.png" alt="Line and area widgets total as well as multi-series per country" class="wp-image-45085" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.7-1.png 1656w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.7-1-300x252.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.7-1-1024x861.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.7-1-768x646.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4b.7-1-1536x1291.png 1536w" sizes="auto, (max-width: 1656px) 100vw, 1656px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<p class="wp-block-paragraph">A few notes that matter in practice:</p>



<ul class="wp-block-list">
<li><strong><em>display.smooth</em></strong> applies to every series. There is no per-series smoothing.</li>



<li><strong><em>display.thresholds</em></strong> are <strong>silently ignored</strong> in multi-series mode. If they were applied per-point, they would override the series palette colors and break the relationship between line color and legend label. So the engine drops them in multi-series and keeps the legend honest.</li>



<li><strong><em>includeEmptyResults</em></strong> in multi-series mode fills gaps across <strong>both dimensions</strong> (every series gets a zero in any time bucket where it has no data). This avoids broken lines.</li>
</ul>



<h3 id="h-7-1-use-low-cardinality-series-only" class="wp-block-heading">7.1. Use low-cardinality series only</h3>



<p class="wp-block-paragraph">The <strong><em>seriesProperty</em></strong> is best used with <strong>low-cardinality</strong> properties: lookup values, booleans, short text ranges. A monthly revenue chart with 5-6 customers is readable, but the same chart with two hundred customers becomes a colored mess where no individual line is distinguishable from the others. If there are too many series, it loses its usefulness. The validation wouldn&#8217;t block it though, since it is technically a valid compatibility. Try it, and see what it looks like!</p>



<h2 id="h-8-includeemptyresults-gap-filling" class="wp-block-heading">8. includeEmptyResults: gap filling</h2>



<p class="wp-block-paragraph">I mentioned this earlier already, and as you could see above, there are actually months with &#8220;0&#8221; values/amounts.</p>



<p class="wp-block-paragraph">With <strong><em>includeEmptyResults: &#8220;Yes&#8221;</em></strong> present, the graph will fill the gaps between the earliest and the latest data points found with zero values. In above example, we can see in March that there was 0 revenue in total. And similarly, in October, there was 0 revenue for the USA. There is no discontinuity in the lines, all months are present, even if there is &#8220;0&#8221; as a result.</p>



<p class="wp-block-paragraph">With <strong><em>includeEmptyResults: &#8220;No&#8221;</em></strong>, the chart will only display the buckets that have at least one matching object (across the series). So, if no invoice was issued in March, the X axis jumps from February to April directly. For above case, it would still display October, since there is a revenue in Switzerland, therefore the USA line will display a &#8220;0&#8221; value still, the line cannot be &#8220;cut&#8221;. Visually, that compresses the time scale and hides the meaningful &#8220;we had no revenue in March&#8221; signal.</p>



<p class="wp-block-paragraph">There are valid use cases for both situations, so just select the one that makes sense depending on what you want to see.</p>



<p class="wp-block-paragraph"><strong><em>Note:</em></strong> Adding <strong><em>includeEmptyResults: &#8220;Yes&#8221;</em></strong> can also add a <strong><em>(none)</em></strong> bucket for objects whose property used by the <strong><em>groupByProperty</em></strong> has no value. For example, if you group by a boolean state Yes/No, and some objects do not have a value on that boolean, they would end up in this <strong><em>(none)</em></strong> group. For <strong><em>groupByDateBucket</em></strong> or <strong><em>groupByRange</em></strong>, they usually appear less frequently since you are grouping things on a certain range, but in the end, it also behaves in the same way if it does happen.</p>



<h2 id="h-9-the-no-date-valued-reducer-rule" class="wp-block-heading">9. The &#8220;no date-valued reducer&#8221; rule</h2>



<p class="wp-block-paragraph">Same rule as on chart widgets in general: <strong><em>line</em></strong> and <strong><em>area</em></strong> cannot render a reducer that returns a date string. If you write something like:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;groupByProperty&quot;,
  &quot;propertyName&quot;: &quot;Customer&quot;,
  &quot;reducer&quot;: &quot;max&quot;,
  &quot;reducerProperty&quot;: &quot;Invoice date&quot;
}
</pre></div>


<p class="wp-block-paragraph">Then, with a <strong><em>line</em></strong> widget, the display would contain a placeholder and the query validation would show a warning. The reason was discussed in the previous post: ISO dates cannot be plotted on a numeric axis at the moment, so it wouldn&#8217;t make sense as a display.</p>



<p class="wp-block-paragraph">The workaround is the same: use <strong><em>kpiNumber</em></strong> or a <strong><em>gauge</em></strong> in date mode if you only want to display one value, or a <strong><em>table</em></strong> for one or more values.</p>



<h2 id="h-10-drill-through-on-trend-widgets" class="wp-block-heading">10. Drill-through on trend widgets</h2>



<p class="wp-block-paragraph">When <strong><em>drillThroughEnabled</em></strong> is <strong><em>&#8220;Yes&#8221;</em></strong>, clicking a data point opens the drill-through modal with the objects in that bucket. In multi-series mode, the click respects the series the point belongs to: only objects matching both the time bucket <strong>and</strong> the series value are shown. This makes it easy to answer questions like &#8220;show me the May invoices from Switzerland&#8221; with a single click on the right point of the line.</p>



<h2 id="h-11-wrap-up" class="wp-block-heading">11. Wrap-up</h2>



<p class="wp-block-paragraph"><strong><em>line</em></strong> and <strong><em>area</em></strong> are the natural choice for time-series questions. They are fundamentally one widget with two visual modes, sharing the same aggregation contract and the same <strong><em>display</em></strong> vocabulary. The interesting part is what you can do with <strong><em>seriesProperty</em></strong>: turning a single trend into a multi-actor comparison without writing any extra logic.</p>



<p class="wp-block-paragraph">Post <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/" id="45109" target="_blank" rel="noreferrer noopener">4c</a> picks up the remaining three widgets: <strong><em>donut</em></strong>, <strong><em>bar</em></strong>, and <strong><em>table</em></strong>. The grouping rationale will be that they all answer <strong>distribution</strong> questions (how a population breaks down by category) or <strong>tabular</strong> questions (give me the raw rows), as opposed to the time-trend questions that <strong><em>line</em></strong> and <strong><em>area</em></strong> handle.</p>



<p class="wp-block-paragraph">Want to know more about this Business Dashboard? <a href="https://www.dbi-services.com/company/contact/" target="_blank" rel="noreferrer noopener">Contact us</a> and we will be happy to showcase it on <a href="https://www.m-files.com/" target="_blank" rel="noreferrer noopener">M-Files</a>.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/">M-Files BD &#8211; Trend widgets: line and area</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>M-Files BD &#8211; Scalar widgets: kpiNumber and gauge</title>
		<link>https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/</link>
					<comments>https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/#respond</comments>
		
		<dc:creator><![CDATA[Morgan Patou]]></dc:creator>
		<pubDate>Tue, 09 Jun 2026 18:09:44 +0000</pubDate>
				<category><![CDATA[Enterprise content management]]></category>
		<category><![CDATA[Business Dashboard]]></category>
		<category><![CDATA[Gauge]]></category>
		<category><![CDATA[KPI]]></category>
		<category><![CDATA[kpiNumber]]></category>
		<category><![CDATA[M-Files]]></category>
		<category><![CDATA[widget]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=45018</guid>

					<description><![CDATA[<p>This is the first widget-specific post of the series. As a reminder, the previous post mapped the anatomy of a dashboard definition. From here on, I go widget by widget. I have grouped the seven widget types into three families: The reason kpiNumber and gauge sit together is that they accept exactly the same aggregation [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/">M-Files BD &#8211; Scalar widgets: kpiNumber and gauge</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">This is the first widget-specific post of the series. As a reminder, the previous post mapped the <a href="https://www.dbi-services.com/blog/m-files-bd-anatomy-of-a-dashboard-definition/" target="_blank" rel="noreferrer noopener">anatomy of a dashboard definition</a>. From here on, I go widget by widget. I have grouped the seven widget types into three families:</p>



<ul class="wp-block-list">
<li><strong>Scalar widgets</strong>: <strong><em>kpiNumber</em></strong> and <strong><em>gauge</em></strong> both display a single value (a count, a sum, a date) over the matching object set. This is what the current post covers.</li>



<li><strong>Trend widgets</strong>: <strong><em>line</em></strong> and <strong><em>area</em></strong>, which are very similar both in form and in configuration, will be the next post, <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" id="45076" target="_blank" rel="noreferrer noopener">4b</a>.</li>



<li><strong>Distribution and tabular widgets</strong>: <strong><em>donut</em></strong>, <strong><em>bar</em></strong>, <strong><em>table</em></strong>, which are all remaining supported widgets (as of now), c.f. Post <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/" id="45109" target="_blank" rel="noreferrer noopener">4c</a> later.</li>
</ul>



<p class="wp-block-paragraph">The reason <strong><em>kpiNumber</em></strong> and <strong><em>gauge</em></strong> sit together is that they accept exactly the same aggregation type (<strong><em>summary</em></strong>) and they share the same reducer model. So once you understand one, the other is mostly a different visual.</p>



<h2 id="h-1-the-summary-aggregation-in-one-paragraph" class="wp-block-heading">1. The summary aggregation in one paragraph</h2>



<p class="wp-block-paragraph">A full post will be dedicated to aggregations (<a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/" id="45206" target="_blank" rel="noreferrer noopener">Post 6</a> in the series), but to make this one self-contained: the <strong><em>summary</em></strong> aggregation returns a single value over all the matching objects. By default, it counts all matching objects. Its initial name was actually &#8220;count&#8221;, but later I wanted to introduce mathematical capabilities (c.f. the next sentence) so I had to change the name. With a <strong><em>reducer</em></strong> of <strong><em>sum</em></strong>, <strong><em>avg</em></strong>, <strong><em>median</em></strong>, <strong><em>min</em></strong>, or <strong><em>max</em></strong> (instead of the default <strong><em>count</em></strong>), it reduces a property&#8217;s values across the matching set instead.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
// Total count of matching objects
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;summary&quot;
}

// Sum of an &quot;amount&quot; property for all matching objects
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;summary&quot;,
  &quot;reducer&quot;: &quot;sum&quot;,
  &quot;reducerProperty&quot;: &quot;Amount&quot;
}

// Latest contract expiry date among all matching objects
&quot;aggregation&quot;: {
  &quot;type&quot;: &quot;summary&quot;,
  &quot;reducer&quot;: &quot;max&quot;,
  &quot;reducerProperty&quot;: &quot;Effective through&quot;
}
</pre></div>


<p class="wp-block-paragraph">Both <strong><em>kpiNumber</em></strong> and <strong><em>gauge</em></strong> accept this aggregation and only this one. The validator&#8217;s compatibility matrix is strict on that point. If you ever try to put a <strong><em>groupByProperty</em></strong> (or something else) on a KPI tile, you will not be able to save your dashboard.</p>



<h2 id="h-2-kpinumber-the-big-number-tile" class="wp-block-heading">2. kpiNumber &#8211; the big number tile</h2>



<p class="wp-block-paragraph">The <strong><em>kpiNumber</em></strong> is the simplest widget in the catalog. It shows a large number, with an optional small unit label below.</p>



<p class="wp-block-paragraph">Minimal example:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{
  &quot;id&quot;: &quot;&quot;,
  &quot;title&quot;: &quot;All Contracts&quot;,
  &quot;type&quot;: &quot;kpiNumber&quot;,
  &quot;gridColumnSpan&quot;: 3,
  &quot;gridRowSpan&quot;: 1,
  &quot;query&quot;: {
    &quot;objectType&quot;: &quot;Document&quot;,
    &quot;class&quot;: &quot;Contract or Agreement&quot;,
    &quot;aggregation&quot;: { &quot;type&quot;: &quot;summary&quot; }
  },
  &quot;display&quot;: { &quot;unit&quot;: &quot;contracts&quot; }
}
</pre></div>


<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1218688e&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1218688e" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1658" height="240" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.2-2.png" alt="M-Files Busisiness Dashboard kpiNumber" class="wp-image-45133" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.2-2.png 1658w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.2-2-300x43.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.2-2-1024x148.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.2-2-768x111.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.2-2-1536x222.png 1536w" sizes="auto, (max-width: 1658px) 100vw, 1658px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h3 id="h-2-1-display-unit" class="wp-block-heading">2.1. display.unit</h3>



<p class="wp-block-paragraph">A small text label shown below the number (c.f. above screenshot), indicating the natural unit of the value: <strong><em>&#8220;contracts&#8221;</em></strong>, <strong><em>&#8220;invoices&#8221;</em></strong>, <strong><em>&#8220;CHF&#8221;</em></strong>, <strong><em>&#8220;open items&#8221;</em></strong> etc&#8230; This is purely optional, so you can omit it if the title is already self-explanatory.</p>



<h3 id="h-2-2-display-decimals" class="wp-block-heading">2.2. display.decimals</h3>



<p class="wp-block-paragraph">Number of decimal to use for the widget (default <strong><em>0</em></strong>). This is useful when the reducer isn&#8217;t the default <strong><em>count</em></strong>, since a count of object is mandatorily an integer. For example:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;display&quot;: { &quot;unit&quot;: &quot;CHF&quot;, &quot;decimals&quot;: 2 }
</pre></div>


<h3 id="h-2-3-display-thresholds" class="wp-block-heading">2.3. display.thresholds</h3>



<p class="wp-block-paragraph">This one is where the KPI tile becomes really useful in management dashboards. The <strong><em>thresholds</em></strong> array colors the number based on the current value. You can either use texts representing colors or the HTML color code:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;display&quot;: {
  &quot;unit&quot;: &quot;Overdue Invoices&quot;,
  &quot;thresholds&quot;: &#x5B;
    { &quot;value&quot;: 0,  &quot;color&quot;: &quot;green&quot;   },
    { &quot;value&quot;: 1, &quot;color&quot;: &quot;orange&quot;  },
    { &quot;value&quot;: 10, &quot;color&quot;: &quot;red&quot; }
  ]
}
</pre></div>


<p class="wp-block-paragraph">The engine applies the color of the highest threshold where <strong><em>currentValue &gt;= threshold.value</em></strong>. So in the example above:</p>



<ul class="wp-block-list">
<li>0 overdue invoices: green</li>



<li>1 to 9: orange</li>



<li>10 and above: red</li>



<li>Below 0 (which would be unusual for a count, but possible for a sum on a money property): falls back to brand blue (<strong><em>#006eef</em></strong> &#8211; which can be changed with a configuration on Admin side) with the above example. But nothing prevents you to set a threshold on negative values.</li>
</ul>



<p class="wp-block-paragraph">Please note that thresholds apply only to numeric values. When the KPI shows a date (e.g. <strong><em>max</em></strong> on a date property), thresholds are ignored and the brand color stays. That&#8217;s the current behavior, and it might change if there is a need that calls for it.</p>



<h3 id="h-2-4-reducer-types-on-kpinumber" class="wp-block-heading">2.4. Reducer types on kpiNumber</h3>



<p class="wp-block-paragraph">All six reducers are supported, with the standard property-type rules:</p>



<ul class="wp-block-list">
<li><strong><em>count</em></strong> <em>(default)</em>: count the number of objects and you do not need to specify the <strong><em>reducerProperty</em></strong> in this case, since it&#8217;s taken by default.</li>



<li><strong><em>sum</em></strong>, <strong><em>avg</em></strong>, <strong><em>median</em></strong>: numeric / time properties.</li>



<li><strong><em>min</em></strong>, <strong><em>max</em></strong>: numeric properties <strong>or</strong> date / timestamp / time properties.</li>
</ul>



<p class="wp-block-paragraph">A few real-world combinations:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
// Count
{ &quot;type&quot;: &quot;summary&quot; }

// Total revenue
{ &quot;type&quot;: &quot;summary&quot;, &quot;reducer&quot;: &quot;sum&quot;, &quot;reducerProperty&quot;: &quot;Amount&quot; }

// Average duration
{ &quot;type&quot;: &quot;summary&quot;, &quot;reducer&quot;: &quot;avg&quot;, &quot;reducerProperty&quot;: &quot;Project duration days&quot; }

// Latest contract expiry
{ &quot;type&quot;: &quot;summary&quot;, &quot;reducer&quot;: &quot;max&quot;, &quot;reducerProperty&quot;: &quot;Effective through&quot; }

// Oldest open invoice
{ &quot;type&quot;: &quot;summary&quot;, &quot;reducer&quot;: &quot;min&quot;, &quot;reducerProperty&quot;: &quot;Invoice date&quot; }
</pre></div>


<p class="wp-block-paragraph">The last two return ISO date strings internally, which is then displayed on the end-user side as <strong><em>DD/MM/YYYY</em></strong> (or whatever matches your regional settings like MM/DD/YYYY in the US for example). So, in M-Files Sample Vault you can build a KPI like &#8220;Latest contract expiry: 31/12/2026&#8221; with three lines of JSON.</p>



<h3 id="h-2-5-drill-through-on-kpinumber" class="wp-block-heading">2.5. Drill-through on kpiNumber</h3>



<p class="wp-block-paragraph">When <strong><em>drillThroughEnabled</em></strong> is <strong><em>&#8220;Yes&#8221;</em></strong> at the dashboard level, the entire KPI tile is clickable. A click opens the drill-through modal with all the objects that contributed to the value. This is useful for all types of reducers, to get more details on how the dashboard reached that value.</p>



<h2 id="h-3-gauge-the-dial" class="wp-block-heading">3. gauge &#8211; the dial</h2>



<p class="wp-block-paragraph">The <strong><em>gauge</em></strong> is also driven by a <strong><em>summary</em></strong> aggregation, but it shows the value on a dial against a defined range. It comes in two flavors depending on the property selected for the reducer.</p>



<h3 id="h-3-1-numeric-mode" class="wp-block-heading">3.1. Numeric mode</h3>



<p class="wp-block-paragraph">This is the default mode and it can be used with any reducer: <strong><em>count</em></strong>, <strong><em>sum</em></strong>, <strong><em>avg</em></strong>, <strong><em>median</em></strong>, or <strong><em>min</em></strong> / <strong><em>max</em></strong> on a numeric property. The scale is in the natural units of the value, with <strong><em>display.min</em></strong> and <strong><em>display.max</em></strong> bracketing the dial.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{
  &quot;id&quot;: &quot;&quot;,
  &quot;title&quot;: &quot;Overdue Invoices&quot;,
  &quot;type&quot;: &quot;gauge&quot;,
  &quot;gridColumnSpan&quot;: 6,
  &quot;gridRowSpan&quot;: 2,
  &quot;display&quot;: { &quot;min&quot;: 0, &quot;max&quot;: 50, &quot;unit&quot;: &quot;invoices&quot; },
  &quot;query&quot;: {
    &quot;objectType&quot;: &quot;Document&quot;,
    &quot;class&quot;: &quot;Purchase Invoice&quot;,
    &quot;filters&quot;: &#x5B;
      { &quot;property&quot;: &quot;Deadline&quot;, &quot;operator&quot;: &quot;lessThan&quot;,
        &quot;value&quot;: &quot;@today&quot;, &quot;valueType&quot;: &quot;dateToken&quot; }
    ],
    &quot;aggregation&quot;: { &quot;type&quot;: &quot;summary&quot; }
  }
}
</pre></div>


<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab12187570&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab12187570" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="836" height="474" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.3.1-2.png" alt="Gauge showing the overdue invoices" class="wp-image-45134" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.3.1-2.png 836w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.3.1-2-300x170.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.3.1-2-768x435.png 768w" sizes="auto, (max-width: 836px) 100vw, 836px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h3 id="h-3-2-display-thresholds-on-a-gauge" class="wp-block-heading">3.2. display.thresholds on a gauge</h3>



<p class="wp-block-paragraph">Same shape as the <strong><em>kpiNumber</em></strong>, but the gauge applies the colors to both the <strong>pointer</strong> and the <strong>arc segments</strong>, so the dial visually splits into zones:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;display&quot;: {
  &quot;min&quot;: 0, &quot;max&quot;: 50, &quot;unit&quot;: &quot;invoices&quot;,
  &quot;thresholds&quot;: &#x5B;
    { &quot;value&quot;: 0, &quot;color&quot;: &quot;green&quot;  },
    { &quot;value&quot;: 5, &quot;color&quot;: &quot;orange&quot;  },
    { &quot;value&quot;: 20, &quot;color&quot;: &quot;red&quot; }
  ]
}
</pre></div>


<p class="wp-block-paragraph">This colors the arc as: 0 to 5 in green, 5 to 20 in orange, 20 and above in red. The pointer also takes the color of the zone where the current value lands. In other words, a glance at the dial tells you whether you are in a comfortable zone, a warning zone, or a critical one (c.f. above screenshot for the example with the colors).</p>



<h3 id="h-3-3-date-mode" class="wp-block-heading">3.3. Date mode</h3>



<p class="wp-block-paragraph">When the <strong><em>reducer</em></strong> is <strong><em>min</em></strong> or <strong><em>max</em></strong> and <strong><em>reducerProperty</em></strong> is a date / timestamp / time property, the gauge switches to <strong>date mode</strong> automatically. The dial then represents a day offset relative to today:</p>



<ul class="wp-block-list">
<li><strong><em>display.min</em></strong> and <strong><em>display.max</em></strong> are, for date / timestamp properties expressed in <strong>day units</strong>, where <strong><em>0</em></strong> is today, negative values are in the past, positive values are in the future. For time properties, it is expressed in <strong>hours units</strong>, where <strong><em>0</em></strong> is midnight and <strong><em>24</em></strong> is, well, also midnight, but the next one ;).</li>



<li>Graduation labels show dates like <strong><em>DD MMM</em></strong> if all dates are within a 365 timeframe, or it adds the <strong><em>YY</em></strong> if it goes beyond, so you know which date exactly we are talking about (depends on your regional settings). For time specifically, the graduation switch automatically to <strong><em>HH:mm</em></strong> display.</li>



<li>The center detail shows the actual date <strong><em>DD/MM/YYYY</em></strong> (in your regional display preference) that comes from the reducer (again, it respects your date regional settings). Or <strong><em>DD/MM/YYYY HH:mm</em></strong> for timestamps or <em><strong>HH:mm:ss</strong></em> for time properties.</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{
  &quot;id&quot;: &quot;&quot;,
  &quot;title&quot;: &quot;Closest Expiry&quot;,
  &quot;type&quot;: &quot;gauge&quot;,
  &quot;gridColumnSpan&quot;: 6,
  &quot;gridRowSpan&quot;: 2,
  &quot;display&quot;: { &quot;min&quot;: 0, &quot;max&quot;: 180, &quot;unit&quot;: &quot;next expiry&quot; },
  &quot;query&quot;: {
    &quot;objectType&quot;: &quot;Document&quot;,
    &quot;class&quot;: &quot;Contract or Agreement&quot;,
    &quot;filters&quot;: &#x5B;
      {
        &quot;property&quot;: &quot;Effective through&quot;,
        &quot;operator&quot;: &quot;greaterOrEqual&quot;,
        &quot;value&quot;: &quot;@today&quot;,
        &quot;valueType&quot;: &quot;dateToken&quot;
      }
    ],
    &quot;aggregation&quot;: {
      &quot;type&quot;: &quot;summary&quot;,
      &quot;reducer&quot;: &quot;min&quot;,
      &quot;reducerProperty&quot;: &quot;Effective through&quot;
    }
  },
}
</pre></div>


<p class="wp-block-paragraph">In this example, the dial spans from today to 180 days in the future. If the earliest contract expiry is 22 days from today, the needle lands at the date that represents 22 and the center shows the actual date. If it is already in the past (negative offset), it indicates an overdue situation immediately. (in below screenshot, I&#8217;m using dates related to the past, as that&#8217;s what is available in the Sample Vault from M-Files, but in a real-world, the above JSON is usable)</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab12187d75&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab12187d75" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="836" height="470" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.3.3-1.png" alt="M-Files Busisiness Dashboard gauge date-mode" class="wp-image-45135" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.3.3-1.png 836w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.3.3-1-300x169.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/4a.3.3-1-768x432.png 768w" sizes="auto, (max-width: 836px) 100vw, 836px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h3 id="h-3-4-when-the-reducer-cannot-produce-a-numeric-value" class="wp-block-heading">3.4. When the reducer cannot produce a numeric value</h3>



<p class="wp-block-paragraph">If you try to use a date reducer on a chart widget (<strong><em>bar</em></strong>, <strong><em>line</em></strong>, <strong><em>area</em></strong>, <strong><em>donut</em></strong>), the widget will show a grey placeholder text, explaining that it is not supported (doesn&#8217;t really make sense). Moreover, before that, the administrator that designed the dashboard also got the same warning that this isn&#8217;t something that would work.</p>



<p class="wp-block-paragraph">On a <strong><em>gauge</em></strong>, you have the date-mode escape hatch. On a <strong><em>kpiNumber</em></strong> or a <strong><em>table</em></strong>, the value is just formatted as a date string. So as a rule, <strong>date-valued</strong> reducers belong on <strong><em>kpiNumber</em></strong>, <strong><em>table</em></strong>, or <strong><em>gauge</em></strong> (date mode). They do not belong on chart widgets.</p>



<p class="wp-block-paragraph">As said, this rule is not arbitrary, it is what the engine actually enforces. The &#8220;Test Widget&#8221;, &#8220;Test Queries&#8221; or &#8220;Save&#8221; will all tell you the same thing in a friendly way, if you slip or forgot about it.</p>



<h2 id="h-4-summary-of-reducers-for-scalar-widgets" class="wp-block-heading">4. Summary of reducers for scalar widgets</h2>



<p class="wp-block-paragraph">To close this post, here is a quick reference of what works on <strong><em>kpiNumber</em></strong> and <strong><em>gauge</em></strong>:</p>



<figure class="wp-block-table"><table><thead><tr><th>Reducer</th><th>What it computes</th><th>reducerProperty types</th><th>Empty set returns</th></tr></thead><tbody><tr><td><strong><em>count</em></strong> <em>(default)</em></td><td>Number of matching objects</td><td>n/a</td><td><strong><em>0</em></strong></td></tr><tr><td><strong><em>sum</em></strong></td><td>Total of the property&#8217;s values</td><td>Numeric or time</td><td><strong><em>null</em></strong> (rendered as <strong><em>&#8211;</em></strong>)</td></tr><tr><td><strong><em>avg</em></strong></td><td>Arithmetic mean</td><td>Numeric or time</td><td><strong><em>null</em></strong> (rendered as <strong><em>&#8211;</em></strong>)</td></tr><tr><td><strong><em>median</em></strong></td><td>Middle value</td><td>Numeric or time</td><td><strong><em>null</em></strong> (rendered as <strong><em>&#8211;</em></strong>)</td></tr><tr><td><strong><em>min</em></strong></td><td>Smallest value</td><td>Numeric or date / timestamp / time</td><td><strong><em>null</em></strong> (rendered as <strong><em>&#8211;</em></strong>)</td></tr><tr><td><strong><em>max</em></strong></td><td>Largest value</td><td>Numeric or date / timestamp / time</td><td><strong><em>null</em></strong> (rendered as <strong><em>&#8211;</em></strong>)</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">As described above, there is a subtle difference of &#8220;0&#8221; vs &#8220;-&#8221; for the empty results. For the default <strong><em>count</em></strong>, we display &#8220;0&#8221;, as there was really no objects returned, so the count of them is correct. For all other reducers, if there is no results, then there is nothing to do maths on. To differentiate between &#8220;no results&#8221; and &#8220;sum is really 0&#8221; (e.g. -2+2), I decided to display &#8220;-&#8221; for cases where there is no actual data to compute.</p>



<p class="wp-block-paragraph">Of course, this empty results set difference is only true when there are no data returned. So if your filter is accurate and you expect some data, then it will never happen.</p>



<h2 id="h-5-closing-thoughts" class="wp-block-heading">5. Closing thoughts</h2>



<p class="wp-block-paragraph"><strong><em>kpiNumber</em></strong> and <strong><em>gauge</em></strong> are the two widgets that typically stands at the top of a dashboard. It can be in a row of four KPIs and a gauge below, or side-by-side, depending on the layout defined. They give the at-a-glance answer that motivated the whole module in the first place.</p>



<p class="wp-block-paragraph">In the next post (<a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" id="45076" target="_blank" rel="noreferrer noopener">4b</a>), I will talk about the trend widgets, <strong><em>line</em></strong> and <strong><em>area</em></strong>, which work in pairs almost as closely as <strong><em>kpiNumber</em></strong> and <strong><em>gauge</em></strong> do here.</p>



<p class="wp-block-paragraph">Want to know more about this Business Dashboard? <a href="https://www.dbi-services.com/company/contact/" target="_blank" rel="noreferrer noopener">Contact us</a> and we will be happy to showcase it on <a href="https://www.m-files.com/" target="_blank" rel="noreferrer noopener">M-Files</a>.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/">M-Files BD &#8211; Scalar widgets: kpiNumber and gauge</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>M-Files BD &#8211; Anatomy of a dashboard definition</title>
		<link>https://www.dbi-services.com/blog/m-files-bd-anatomy-of-a-dashboard-definition/</link>
					<comments>https://www.dbi-services.com/blog/m-files-bd-anatomy-of-a-dashboard-definition/#respond</comments>
		
		<dc:creator><![CDATA[Morgan Patou]]></dc:creator>
		<pubDate>Sun, 07 Jun 2026 14:52:03 +0000</pubDate>
				<category><![CDATA[Enterprise content management]]></category>
		<category><![CDATA[Business Dashboard]]></category>
		<category><![CDATA[Dashboard]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[M-Files]]></category>
		<category><![CDATA[widget]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=44952</guid>

					<description><![CDATA[<p>In the previous post, I walked through what an end user sees when they open the Business Dashboard pane in M-Files. From this post on, the audience shifts more towards the administrator&#8217;s side, looking at what a dashboard definition looks like. Every dashboard is a single JSON document. The Admin tab in M-Files Admin offers [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-anatomy-of-a-dashboard-definition/">M-Files BD &#8211; Anatomy of a dashboard definition</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In the <a href="https://www.dbi-services.com/blog/m-files-bd-end-user-experience/" id="44932" target="_blank" rel="noreferrer noopener">previous post</a>, I walked through what an end user sees when they open the Business Dashboard pane in M-Files. From this post on, the audience shifts more towards the administrator&#8217;s side, looking at what a dashboard definition looks like.</p>



<p class="wp-block-paragraph">Every dashboard is a single JSON document. The Admin tab in M-Files Admin offers a Visual Designer that builds this JSON for you, but in the end the JSON is the source of truth, so I think it is worth understanding its shape before getting into widget types and queries in the next posts. Think of this post as the <strong>reference map</strong> the rest of the series will refer back to.</p>



<h2 id="h-1-the-minimum-valid-dashboard-definition" class="wp-block-heading">1. The minimum valid dashboard definition</h2>



<p class="wp-block-paragraph">Before discussing every field, here is the (almost) smallest dashboard JSON that the engine accepts. It is intentionally trivial &#8211; a single KPI widget counting all Documents in the vault:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{
  &quot;schemaVersion&quot;: 1,
  &quot;id&quot;: &quot;&quot;,
  &quot;name&quot;: &quot;My Dashboard&quot;,
  &quot;widgets&quot;: &#x5B;
    {
      &quot;id&quot;: &quot;&quot;,
      &quot;title&quot;: &quot;Total Documents&quot;,
      &quot;type&quot;: &quot;kpiNumber&quot;,
      &quot;query&quot;: {
        &quot;objectType&quot;: &quot;Document&quot;,
        &quot;aggregation&quot;: { &quot;type&quot;: &quot;summary&quot; }
      }
    }
  ]
}
</pre></div>


<p class="wp-block-paragraph"><strong>Note 1:</strong> Boolean-like fields are exposed as the strings <strong><em>&#8220;Yes&#8221;</em></strong> or <strong><em>&#8220;No&#8221;</em></strong>. This is what M-Files shows as booleans, don&#8217;t ask me why it&#8217;s not True/False like the rest of the world ;).</p>



<p class="wp-block-paragraph"><strong>Note 2:</strong> Display names (object types, classes, properties, value-list items) must match what the vault shows in M-Files Admin. They are case-insensitive but must otherwise be exact.</p>



<h2 id="h-2-the-dashboard-level-fields" class="wp-block-heading">2. The dashboard-level fields</h2>



<p class="wp-block-paragraph">The top-level object can carry the following fields: required ones are marked, all others are optional with default values that make sense, usually.</p>



<h3 id="h-2-1-identification-and-presentation" class="wp-block-heading">2.1. Identification and presentation</h3>



<p class="wp-block-paragraph">These are the main parameters of a dashboard that you will usually set at the beginning:</p>



<ul class="wp-block-list">
<li><strong><em>schemaVersion</em></strong>: Integer (currently <strong><em>1</em></strong>). The <strong>Business Dashboard</strong> validation process emits a warning when this field is absent or lower than expected. This version is incremented when breaking changes are introduced, so admins will automatically be flagged that they need to update something.</li>



<li><strong><em>id</em></strong> <em>(required)</em>: typically a UUID v4 value (e.g. <strong><em>&#8220;12345678-abcd-1234-5678-abcd12345678&#8221;</em></strong>) but it isn&#8217;t restricted to that (e.g. <strong><em>&#8220;d01-contracts&#8221;</em></strong> works as well). The ID should be unique for each dashboard, otherwise you risk overriding an existing dashboard when you save. You can leave the field ID empty (as done above), the <strong>Business Dashboard</strong> will automatically generate one for you.</li>



<li><strong><em>name</em></strong> <em>(required)</em>: Text shown in the end-user dropdown, you should keep it concise (&lt;250 chars).</li>



<li><strong><em>description</em></strong>: Optional subtitle shown in italic below the top bar (hidden when absent). As described in the <a href="https://www.dbi-services.com/blog/m-files-bd-end-user-experience/" id="44932" target="_blank" rel="noreferrer noopener">previous post</a>, the first two lines will be shown on the user side, with a hover tooltip for the full text.</li>
</ul>



<h3 id="h-2-2-auto-refresh" class="wp-block-heading">2.2. Auto-refresh</h3>



<p class="wp-block-paragraph">These are parameters that controls whether or not the dashboard will refresh itself or allows controls about the refresh more globally:</p>



<ul class="wp-block-list">
<li><strong><em>autoRefreshEnabled</em></strong>: <strong><em>&#8220;Yes&#8221;</em></strong> or <strong><em>&#8220;No&#8221;</em></strong> (default <strong><em>&#8220;No&#8221;</em></strong>). Whether auto-refresh is enabled when the dashboard first loads.</li>



<li><strong><em>autoRefreshIntervalSeconds</em></strong>: Integer (default <strong><em>300</em></strong>, minimum <strong><em>15</em></strong>). Typical values can be <strong><em>60</em></strong>, <strong><em>300</em></strong> or <strong><em>900</em></strong> for example. This is the interval of time between two auto-refresh of all widgets done when the auto-refresh is enabled (either via <strong><em>autoRefreshEnabled</em></strong> or because <strong><em>userCanToggleAutoRefresh</em></strong> is <strong><em>&#8220;Yes&#8221;</em></strong> and a user enabled it manually). The minimum is set to 15s to avoid overloading the vault in case of mis-configuration.</li>



<li><strong><em>userCanToggleAutoRefresh</em></strong>: <strong><em>&#8220;Yes&#8221;</em></strong> or <strong><em>&#8220;No&#8221;</em></strong> (default <strong><em>&#8220;No&#8221;</em></strong>). Whether the user can disable or enable the auto-refresh feature.</li>
</ul>



<h3 id="h-2-3-exports-and-drill-through" class="wp-block-heading">2.3. Exports and drill-through</h3>



<p class="wp-block-paragraph">These are the optional features that you can enable:</p>



<ul class="wp-block-list">
<li><strong><em>exportToPdfEnabled</em></strong>: <strong><em>&#8220;Yes&#8221;</em></strong> or <strong><em>&#8220;No&#8221;</em></strong> (default <strong><em>&#8220;No&#8221;</em></strong>). Shows the ⎙ PDF button in the top bar, to allow users to export the dashboard to PDF.</li>



<li><strong><em>exportToCsvEnabled</em></strong>: <strong><em>&#8220;Yes&#8221;</em></strong> or <strong><em>&#8220;No&#8221;</em></strong> (default <strong><em>&#8220;No&#8221;</em></strong>). Shows the ⊞ CSV button on all widgets (except kpiNumber &amp; gauge) as well as on the drill-through modal, to allow users to export the widget details to CSV.</li>



<li><strong><em>drillThroughEnabled</em></strong>: <strong><em>&#8220;Yes&#8221;</em></strong> or <strong><em>&#8220;No&#8221;</em></strong> (default <strong><em>&#8220;No&#8221;</em></strong>). Makes chart elements and rows clickable to open the drill-through modal (c.f. <a href="https://www.dbi-services.com/blog/m-files-bd-end-user-experience/" id="44932" target="_blank" rel="noreferrer noopener">previous post</a> if you don&#8217;t know what the drill-through is).</li>
</ul>



<p class="wp-block-paragraph">These three flags are intentionally off by default. Turning them on is an explicit choice per dashboard, which I think is the right place to make that decision. A dashboard meant for casual &#8220;at-a-glance&#8221; use might not need them while a dashboard meant for active investigation might.</p>



<h3 id="h-2-4-performance-settings" class="wp-block-heading">2.4. Performance settings</h3>



<p class="wp-block-paragraph">These are security and performance settings:</p>



<ul class="wp-block-list">
<li><strong><em>skipTemplateCheck</em></strong>: <strong><em>&#8220;Yes&#8221;</em></strong> or <strong><em>&#8220;No&#8221;</em></strong> (default <strong><em>&#8220;No&#8221;</em></strong>). When <strong><em>&#8220;No&#8221;</em></strong> (default), objects marked as template (i.e. they have the &#8220;is Template&#8221; property and it is checked) are automatically excluded from any results. When <strong><em>&#8220;Yes&#8221;</em></strong>, this additional verification is skipped and templates can therefore be part of the results (faster execution).</li>



<li><strong><em>skipObjectPermissionCheck</em></strong>: <strong><em>&#8220;Yes&#8221;</em></strong> or <strong><em>&#8220;No&#8221;</em></strong> (default <strong><em>&#8220;No&#8221;</em></strong>). When <strong><em>&#8220;No&#8221;</em></strong> (default), every queries will respect the user-level accesses / ACLs, so different users might see different results / numbers / lists of objects, depending on their permissions. When <strong><em>&#8220;Yes&#8221;</em></strong>, server-level accesses are used, so different users will see the same results, always (faster execution).</li>



<li><strong><em>serverScanMaxResults</em></strong>: Integer (default <strong><em>500</em></strong>). The maximum number of M-Files objects fetched per widget query. You can set it to <strong><em>0</em></strong> for unlimited.</li>



<li><strong><em>drillThroughMaxResults</em></strong>: Integer (default <strong><em>300</em></strong>). Maximum rows shown in drill-through modals and <strong><em>list</em></strong> tables. You can set it to <strong><em>0</em></strong> for unlimited. The pagination for the opened modal is set at 15 rows per page. Therefore, there can be up to 20 pages by default.</li>
</ul>



<p class="wp-block-paragraph">It will be covered in more details in <a href="https://www.dbi-services.com/blog/m-files-bd-the-admin-part/" id="45312" target="_blank" rel="noreferrer noopener">Post 7</a>, so I will not spend too much time on them here. The defaults are reasonable for most use-cases but you can change everything, of course.</p>



<h3 id="h-2-5-access-control" class="wp-block-heading">2.5. Access control</h3>



<p class="wp-block-paragraph">Finally, the last parameter is about access control to the dashboard:</p>



<ul class="wp-block-list">
<li><strong><em>assignedTo</em></strong>: An object with two optional arrays, <strong><em>users</em></strong> and <strong><em>groups</em></strong>. When the arrays are absent or empty, the dashboard is public. This means that ANY M-Files user will be able to select the dashboard from the dropdown. It does NOT mean that he will see something inside the widgets! Therefore, it gives access to the dashboard itself, but widget content still relies on the user&#8217;s ACLs by default (or server-level access if <strong><em>skipObjectPermissionCheck</em></strong> is set to <strong><em>&#8220;Yes&#8221;</em></strong>, c.f. above section). On the other hand, when at least one of the arrays is present, access is granted to any user who appears in the <strong><em>users</em></strong> array <strong>OR</strong> is a member of any group in the <strong><em>groups</em></strong> array.</li>
</ul>



<p class="wp-block-paragraph">In this example, the dashboard will be accessible by the user whose Login Account is <strong><em>Morgan.Patou</em></strong> OR any users part of the <strong><em>Finance Team</em></strong> OR <strong><em>Management</em></strong> groups:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;assignedTo&quot;: {
  &quot;users&quot;: &#x5B;&quot;Morgan.Patou&quot;],
  &quot;groups&quot;: &#x5B;&quot;Finance Team&quot;, &quot;Management&quot;]
}
</pre></div>


<h3 id="h-2-6-widgets" class="wp-block-heading">2.6. Widgets</h3>



<ul class="wp-block-list">
<li><strong><em>widgets</em></strong> <em>(required)</em>: An array of widget objects. The order in this array is the order they appear on the grid. The size of the widget (column span, row span) is part of each widget&#8217;s definition (i.e. inside the array).</li>
</ul>



<h2 id="h-3-the-widget-object" class="wp-block-heading">3. The widget object</h2>



<p class="wp-block-paragraph">Each entry in the <strong><em>widgets</em></strong> array has the following shape:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{
  &quot;id&quot;: &quot;&quot;,
  &quot;title&quot;: &quot;My Widget&quot;,
  &quot;description&quot;: &quot;An example description for My Widget&quot;,
  &quot;type&quot;: &quot;kpiNumber&quot;,
  &quot;gridColumnSpan&quot;: 3,
  &quot;gridRowSpan&quot;: 1,
  &quot;query&quot;: { ... },
  &quot;display&quot;: { ... }
}
</pre></div>


<p class="wp-block-paragraph">Here is a quick description of the different fields:</p>



<ul class="wp-block-list">
<li><strong><em>id</em></strong> <em>(required)</em>:  Same description as for the dashboard</li>



<li><strong><em>title</em></strong> <em>(required)</em>:  Name of the widget shown at the top of the tile</li>



<li><strong><em>description</em></strong>: Optional one-line italic subtitle just below the title. Annotate what the widget measures, or flag a caveat</li>



<li><strong><em>type</em></strong> <em>(required)</em>: One of <strong><em>kpiNumber</em></strong>, <strong><em>gauge</em></strong>, <strong><em>donut</em></strong>, <strong><em>bar</em></strong>, <strong><em>line</em></strong>, <strong><em>area</em></strong>, <strong><em>table</em></strong> (covered in posts <a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/" id="45018" target="_blank" rel="noreferrer noopener">4a</a>, <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" id="45076" target="_blank" rel="noreferrer noopener">4b</a>, <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/" id="45109" target="_blank" rel="noreferrer noopener">4c</a>)</li>



<li><strong><em>gridColumnSpan</em></strong>: Integer from 1 to 12 (default <strong><em>4</em></strong>). Represent the width of the widget on the dashboard</li>



<li><strong><em>gridRowSpan</em></strong>: Integer (default <strong><em>1</em></strong>). Represent the height of the widget on the dashboard</li>



<li><strong><em>query</em></strong> <em>(required)</em>: The query the widget will execute. This can be pretty complex, so it will be detailed in <a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/" id="45175" target="_blank" rel="noreferrer noopener">Post 5</a> (filters) and <a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/" id="45206" target="_blank" rel="noreferrer noopener">Post 6</a> (aggregations), later</li>



<li><strong><em>display</em></strong>: Type-specific display options, like color thresholds, unit, decimals, etc. It will be covered per widget type in posts 4x as well</li>
</ul>



<h2 id="h-4-the-query-object-in-one-paragraph" class="wp-block-heading">4. The query object in one paragraph</h2>



<p class="wp-block-paragraph">As mentioned, the detailed walkthrough of queries is the topic of <a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/" id="45175" target="_blank" rel="noreferrer noopener">Post 5</a>&#8211;<a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/" id="45206" target="_blank" rel="noreferrer noopener">6</a>, but for completeness, here is the basic shape of a query:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&quot;query&quot;: {
  &quot;objectType&quot;: &quot;Document&quot;,
  &quot;class&quot;: &quot;Contract or Agreement&quot;,
  &quot;filters&quot;: &#x5B;
    {
      &quot;property&quot;: &quot;Effective through&quot;,
      &quot;operator&quot;: &quot;greaterOrEqual&quot;,
      &quot;value&quot;: &quot;@today&quot;,
      &quot;valueType&quot;: &quot;dateToken&quot;
    }
  ],
  &quot;aggregation&quot;: {
    &quot;type&quot;: &quot;groupByProperty&quot;,
    &quot;propertyName&quot;: &quot;Agreement type&quot;,
    &quot;includeEmptyResults&quot;: &quot;No&quot;
  }
}
</pre></div>


<p class="wp-block-paragraph"><strong><em>objectType</em></strong> can be a string or an array of strings (multi-object-type query). <strong><em>class</em></strong> is optional but strongly recommended for performance and to check only objects that actually matter&#8230; For example, you don&#8217;t need to fetch all documents (irrespective of their class) if you only want to count the number of active contracts. <strong><em>filters</em></strong> is an array of 1-to-N filter combined by <strong>AND</strong>. Finally, <strong><em>aggregation</em></strong> specifies the shape of the result and the reduction applied. To know more about these, please wait for the next posts.</p>



<h2 id="h-5-the-12-column-grid" class="wp-block-heading">5. The 12-column grid</h2>



<p class="wp-block-paragraph">The layout is computed on a CSS grid with 12 columns. <strong><em>gridColumnSpan</em></strong> sets the width of a widget, while <strong><em>gridRowSpan</em></strong> sets its height in row units.</p>



<p class="wp-block-paragraph">Therefore, most common column layouts include:</p>



<ul class="wp-block-list">
<li>4 widgets per row: <strong><em>gridColumnSpan: 3</em></strong> each. Typical for KPI tiles.</li>



<li>3 widgets per row: <strong><em>gridColumnSpan: 4</em></strong> each. Medium tiles or narrow charts.</li>



<li>2 widgets per row: <strong><em>gridColumnSpan: 6</em></strong> each. Charts and tables.</li>



<li>1 widget per row: <strong><em>gridColumnSpan: 12</em></strong>. Full-width bar chart or wide table.</li>
</ul>



<p class="wp-block-paragraph">These are just examples. Nothing prevents you to set 3 widgets on a row with <strong><em>gridColumnSpan: 5</em></strong>, <strong><em>gridColumnSpan: 4</em></strong>, <strong><em>gridColumnSpan: 3</em></strong> respectively. The Visual Designer is great for that since you get an exact view of what it will look like directly while creating the dashboard. More on that on the <a href="https://www.dbi-services.com/blog/m-files-bd-putting-it-together-with-the-visual-designer/" id="45433" target="_blank" rel="noreferrer noopener">Post 8</a>.</p>



<p class="wp-block-paragraph">The widgets simply fill the rows from left to right in the order they appear in the <strong><em>widgets</em></strong> array, wrapping to the next row when a row&#8217;s spans sum exceeds 12. So planning the layout is largely a matter of ordering the widgets and giving each one a sensible column span.</p>



<p class="wp-block-paragraph">For row height, a small subtlety: <strong><em>gridRowSpan: 2</em></strong> does not give you exactly twice the canvas height of <strong><em>gridRowSpan: 1</em></strong>. It actually gives you roughly <strong>three times</strong> the canvas height, because each row unit subtracts a fixed overhead (header, padding, borders) from the available space. The practical consequence is that <strong>chart widgets</strong> (<strong><em>bar</em></strong>, <strong><em>line</em></strong>, <strong><em>area</em></strong>, <strong><em>donut</em></strong>, <strong><em>gauge</em></strong>) should almost always use <strong><em>gridRowSpan: 2</em></strong> or higher. A single-row chart has very little drawing space and ends up cramped.</p>



<p class="wp-block-paragraph">KPI number tiles, on the other hand, look fine at <strong><em>gridRowSpan: 1</em></strong> and that is the right default for them. As a rule of thumb: KPIs at 1, everything else at 2.</p>



<h2 id="h-6-where-the-definition-is-stored" class="wp-block-heading">6. Where the definition is stored</h2>



<p class="wp-block-paragraph">Dashboard definitions are stored in the vault&#8217;s <strong>Named Value Storage</strong> (NVS). They are NOT stored as a specific object in the vault. I initially thought about creating them as standard vault objects but in the end decided the NVS would probably make it easier for deployments and usage in the long run. The trade-offs of that choice:</p>



<ul class="wp-block-list">
<li><strong>Pros</strong>: no extra object type to provision when installing the module, no ACL setup, no admin training on a new object type, no per-customer deployment friction. Backups of the vault include the dashboards automatically because NVS is part of the vault.</li>



<li><strong>Cons</strong>: there is no per-definition version history of the kind you would get with vault objects. I mitigated this by offering Import and Export buttons in the Admin tab that produce <strong><em>.json</em></strong> files. What I would recommend is to manage those files in a Git repository, which gives a clean and conventional version history outside the vault, with diff support.</li>
</ul>



<p class="wp-block-paragraph">The Export / Import buttons are the topic of <a href="https://www.dbi-services.com/blog/m-files-bd-the-admin-part/" id="45312" target="_blank" rel="noreferrer noopener">Post 7</a>. The TL;DR is that I would recommend that you should develop your dashboards on a DEV. Then, export them all into your git. Finally, when ready, import them into the TEST/QA/PROD environments, either manually or via CI/CD pipelines.</p>



<h2 id="h-7-quick-overview-of-a-full-dashboard-json-definition" class="wp-block-heading">7. Quick overview of a full dashboard JSON definition</h2>



<p class="wp-block-paragraph">Putting everything together, here is a slightly more complete (but still small) example. It has some of the dashboard-level fields modified from their default values to show-case actions and buttons, and two widgets:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
{
  &quot;schemaVersion&quot;: 1,
  &quot;id&quot;: &quot;&quot;,
  &quot;name&quot;: &quot;Contracts&quot;,
  &quot;description&quot;: &quot;Quick overview of contracts&quot;,
  &quot;autoRefreshEnabled&quot;: &quot;Yes&quot;,
  &quot;autoRefreshIntervalSeconds&quot;: 60,
  &quot;userCanToggleAutoRefresh&quot;: &quot;Yes&quot;,
  &quot;exportToPdfEnabled&quot;: &quot;Yes&quot;,
  &quot;exportToCsvEnabled&quot;: &quot;Yes&quot;,
  &quot;skipTemplateCheck&quot;: &quot;No&quot;,
  &quot;drillThroughEnabled&quot;: &quot;Yes&quot;,
  &quot;skipObjectPermissionCheck&quot;: &quot;No&quot;,
  &quot;serverScanMaxResults&quot;: 500,
  &quot;drillThroughMaxResults&quot;: 300,
  &quot;assignedTo&quot;: {
    &quot;users&quot;: &#x5B;],
    &quot;groups&quot;: &#x5B;&quot;Legal Team&quot;]
  },
  &quot;widgets&quot;: &#x5B;
    {
      &quot;id&quot;: &quot;&quot;,
      &quot;title&quot;: &quot;Active Contracts&quot;,
      &quot;type&quot;: &quot;kpiNumber&quot;,
      &quot;gridColumnSpan&quot;: 3,
      &quot;gridRowSpan&quot;: 2,
      &quot;query&quot;: {
        &quot;objectType&quot;: &quot;Document&quot;,
        &quot;class&quot;: &quot;Contract or Agreement&quot;,
        &quot;filters&quot;: &#x5B;
          {
            &quot;property&quot;: &quot;Effective through&quot;,
            &quot;operator&quot;: &quot;greaterOrEqual&quot;,
            &quot;value&quot;: &quot;@today&quot;,
            &quot;valueType&quot;: &quot;dateToken&quot;
          }
        ],
        &quot;aggregation&quot;: { &quot;type&quot;: &quot;summary&quot; }
      },
      &quot;display&quot;: { &quot;unit&quot;: &quot;contracts&quot; }
    },
    {
      &quot;id&quot;: &quot;&quot;,
      &quot;title&quot;: &quot;By Agreement Type&quot;,
      &quot;type&quot;: &quot;donut&quot;,
      &quot;gridColumnSpan&quot;: 9,
      &quot;gridRowSpan&quot;: 2,
      &quot;query&quot;: {
        &quot;objectType&quot;: &quot;Document&quot;,
        &quot;class&quot;: &quot;Contract or Agreement&quot;,
        &quot;aggregation&quot;: {
          &quot;type&quot;: &quot;groupByProperty&quot;,
          &quot;propertyName&quot;: &quot;Agreement type&quot;,
        }
      }
    }
  ]
}
</pre></div>


<p class="wp-block-paragraph">This dashboard renders as a single KPI tile and a donut chart on the same row (3 + 9 = 12 columns), with auto-refresh every minute and access restricted to the Legal Team group. Nothing exotic, this is the kind of dashboard most customers should probably start with.</p>



<p class="wp-block-paragraph">If you wonder what it would look like, you can check the <a href="https://www.dbi-services.com/blog/m-files-introducing-the-business-dashboard-module/" id="44913" target="_blank" rel="noreferrer noopener">1st</a> and <a href="https://www.dbi-services.com/blog/m-files-bd-end-user-experience/" id="44932" target="_blank" rel="noreferrer noopener">2nd</a> posts, they include these same widgets (though arranged slightly differently).</p>



<h2 id="h-8-what-comes-next" class="wp-block-heading">8. What comes next</h2>



<p class="wp-block-paragraph">In the next three posts (<a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/" id="45018" target="_blank" rel="noreferrer noopener">4a</a>, <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" id="45076" target="_blank" rel="noreferrer noopener">4b</a>, <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/" id="45109" target="_blank" rel="noreferrer noopener">4c</a>) I will go through the seven widget types one family at a time:</p>



<ul class="wp-block-list">
<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/" id="45018" target="_blank" rel="noreferrer noopener">4a</a></strong> &#8211; Scalar widgets: <strong><em>kpiNumber</em></strong> and <strong><em>gauge</em></strong>.</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" id="45076" target="_blank" rel="noreferrer noopener">4b</a></strong> &#8211; Trend widgets: <strong><em>line</em></strong> and <strong><em>area</em></strong>.</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/" id="45109" target="_blank" rel="noreferrer noopener">4c</a></strong> &#8211; Distribution and tabular widgets: <strong><em>donut</em></strong>, <strong><em>bar</em></strong>, <strong><em>table</em></strong>.</li>
</ul>



<p class="wp-block-paragraph">After the widget tour, <a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/" id="45175" target="_blank" rel="noreferrer noopener">Post 5</a> covers the query side (object types, classes, filters, date tokens) and <a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/" id="45206" target="_blank" rel="noreferrer noopener">Post 6</a> covers aggregations and reducers. By the end of those, you will have everything needed to write a non-trivial dashboard from scratch &#8211; if that&#8217;s what you want to do&#8230; Or you can just use the Visual Designer and follow along.</p>



<p class="wp-block-paragraph">In the meantime, I hope this anatomy post gives you a clean mental map. If something is unclear or if you spotted a field whose behavior I have not explained here, please reach out so I can update the post.</p>



<p class="wp-block-paragraph">Want to know more about this Business Dashboard? <a href="https://www.dbi-services.com/company/contact/" target="_blank" rel="noreferrer noopener">Contact us</a> and we will be happy to showcase it on <a href="https://www.m-files.com/" target="_blank" rel="noreferrer noopener">M-Files</a>.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-anatomy-of-a-dashboard-definition/">M-Files BD &#8211; Anatomy of a dashboard definition</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/m-files-bd-anatomy-of-a-dashboard-definition/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>M-Files BD &#8211; End-user experience</title>
		<link>https://www.dbi-services.com/blog/m-files-bd-end-user-experience/</link>
					<comments>https://www.dbi-services.com/blog/m-files-bd-end-user-experience/#respond</comments>
		
		<dc:creator><![CDATA[Morgan Patou]]></dc:creator>
		<pubDate>Thu, 04 Jun 2026 20:48:21 +0000</pubDate>
				<category><![CDATA[Enterprise content management]]></category>
		<category><![CDATA[Business Dashboard]]></category>
		<category><![CDATA[Charts]]></category>
		<category><![CDATA[CSV]]></category>
		<category><![CDATA[drill-through]]></category>
		<category><![CDATA[favorite]]></category>
		<category><![CDATA[Gauge]]></category>
		<category><![CDATA[KPI]]></category>
		<category><![CDATA[M-Files]]></category>
		<category><![CDATA[pdf]]></category>
		<category><![CDATA[widget]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=44932</guid>

					<description><![CDATA[<p>In the first post of this series, I introduced the Business Dashboard module I built, over the past few weeks, for M-Files. This second post is for the end user: what does it actually look like, where is it, and what can you do with it once your administrator has configured one or more dashboards [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-end-user-experience/">M-Files BD &#8211; End-user experience</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In the <a href="https://www.dbi-services.com/blog/m-files-introducing-the-business-dashboard-module/" id="44913" target="_blank" rel="noreferrer noopener">first post of this series</a>, I introduced the Business Dashboard module I built, over the past few weeks, for M-Files. This second post is for the end user: what does it actually look like, where is it, and what can you do with it once your administrator has configured one or more dashboards for you. No JSON in this post, no admin screens, no code. Just your experience as end-users. Please note that in the rest of this blog, when I say &#8220;administrator&#8221;, I mean the person that will design your dashboards. It might be a Super User or a System Admin, but he needs access to the M-Files Admin UI.</p>



<p class="wp-block-paragraph">If you have followed my posts for some time, you know I prefer to walk you through a feature (or an issue) exactly as you would discover it. So, let&#8217;s do the same here.</p>



<h2 id="h-1-where-to-find-the-dashboard-pane" class="wp-block-heading">1. Where to find the dashboard pane</h2>



<p class="wp-block-paragraph">The Business Dashboard adds a third tab to the right-hand pane of M-Files, next to the standard <strong>Metadata</strong> and <strong>Preview</strong> tabs. It is simply labelled <strong>Dashboard</strong>. Both M-Files Web (in a browser) and M-Files Desktop (the Windows client) show it &#8211; same panel, same behavior, with one platform-specific detail I will flag along the way.</p>



<p class="wp-block-paragraph">As a reminder, this is what it looks like and how to access it:</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1219b34a&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1219b34a" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="2560" height="2059" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-6-scaled.png" alt="M-Files home page, with the Business Dashboard panel opened showing one dashboard with multiple widgets" class="wp-image-45122" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-6-scaled.png 2560w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-6-300x241.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-6-1024x823.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-6-768x618.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-6-1536x1235.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-6-2048x1647.png 2048w" sizes="auto, (max-width: 2560px) 100vw, 2560px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<p class="wp-block-paragraph">If you do not see the Dashboard tab, two things are possible: either the module is not installed in your vault, or it is installed but your vault is configured to use the old UI. In both cases, the next step is to ask your administrator.</p>



<p class="wp-block-paragraph">A small note on the pane width: the right pane is resizable by dragging its left edge. The dashboard AND the widgets inside respond to that resize. Therefore, on very narrow widths, widgets like donuts will have their legends move from the side to the bottom, to give more space to the charts. Don&#8217;t hesitate to play with the resize, it can be a good stress-reliever ;).</p>



<h2 id="h-2-selecting-and-switching-between-dashboards" class="wp-block-heading">2. Selecting and switching between dashboards</h2>



<p class="wp-block-paragraph">At the top of the pane, you have a <strong>&#8220;Dashboard:&#8221;</strong> dropdown. It lists every dashboard your administrator has decided you can see, in the order the administrator chose. There might be more of them, but in this case, you don&#8217;t have permissions to see them. Click the dropdown, pick a dashboard, the panel updates immediately.</p>



<p class="wp-block-paragraph">The last dashboard you selected is remembered for the next time you open the pane, so if you mostly use one dashboard (e.g. the Contracts one), you do not need to re-select it every morning. Even better than that, you can set multiple favorites and in this case they appear at the top of the list. More on that &#8220;last seen&#8221; vs &#8220;favorite&#8221; vs &#8220;link&#8221; below, in following sections.</p>



<p class="wp-block-paragraph">Under the dropdown, if your administrator added a description to the dashboard, you will see a thin italic strip that contains it. It is clamped to two lines maximum. If the description is longer, hover the strip to see the full text in a tooltip. In addition to that, when the dashboard dropdown is opened, you will also be able to see the description of the dashboard you hover over, which will have a darker background.</p>



<p class="wp-block-paragraph">In this screenshot, you can see the selected dashboard is about <strong>Contracts</strong> and its description is <strong>Quick view of contracts: active, &#8230;</strong>, while the description of the <strong>Invoices Overview</strong> dashboard is <strong>Purchase and Sales Invoice breakdown by year, workflow state, and customer.</strong>. Therefore, before loading it, you can get a quick idea on what exactly it will contain:</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1219bbf7&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1219bbf7" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1766" height="1382" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.2-2.png" alt="A close up on the dashboard dropdown selector and description" class="wp-image-45124" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.2-2.png 1766w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.2-2-300x235.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.2-2-1024x801.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.2-2-768x601.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.2-2-1536x1202.png 1536w" sizes="auto, (max-width: 1766px) 100vw, 1766px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-3-the-widget-grid" class="wp-block-heading">3. The widget grid</h2>



<p class="wp-block-paragraph">Each dashboard is a grid of widgets. As said, the grid is responsive and it contains 12 columns, so each widget can use from 1/12th and up to 100% of the width. Your administrator decides which widgets appear, in what order, at what size.</p>



<p class="wp-block-paragraph">As of now, there are seven widget types supported:</p>



<figure class="wp-block-table"><table><thead><tr><th>Widget</th><th>What it shows</th></tr></thead><tbody><tr><td><strong>KPI Number</strong></td><td>A single value, typically a count, a sum (e.g. &#8220;Active contracts: 142&#8221;, &#8220;Invoices: 1&#8217;520&#8217;430 CHF&#8221;) or other mathematical operators like min/max/average/median. But it can only display a date (with min/max).</td></tr><tr><td><strong>Gauge</strong></td><td>A dial with a value against a defined range. Some gauges are numeric (e.g. overdue invoices against a target of 0-50). Some are in <strong>date mode</strong>, showing a date value as a day offset from today (&#8220;days until earliest contract expiry&#8221;).</td></tr><tr><td><strong>Donut chart</strong></td><td>A distribution across categories or time periods (e.g. contracts by agreement type). Slice values appear inline. The legend is shown next to the donut when the pane is wide enough, otherwise below. Also supports multi-series by displaying multiple sub-donuts.</td></tr><tr><td><strong>Bar chart</strong></td><td>A distribution across categories or time periods, displayed either vertically or horizontally. Also supports multi-series, with stacked or grouped bars.</td></tr><tr><td><strong>Line chart</strong></td><td>A distribution across categories or time periods, with data points linked to each other. Also supports multi-series, with multiple lines.</td></tr><tr><td><strong>Area chart</strong></td><td>Same data as a line, with the area below the line filled, to emphasise volume.</td></tr><tr><td><strong>Table</strong></td><td>A sortable list of objects or groups of objects, with pagination and additional columns that can be added. </td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Each widget has a title at the top, sometimes a short italic subtitle (the widget description, when your administrator added one), and three small elements in the top-right corner of the widget header that I will detail in the next sections.</p>



<h2 id="h-4-refreshing-the-data" class="wp-block-heading">4. Refreshing the data</h2>



<p class="wp-block-paragraph">Data on the dashboard is fetched live from M-Files. There are three ways the dashboard can be refreshed:</p>



<ul class="wp-block-list">
<li><strong>Auto-refresh.</strong> If your administrator enabled auto-refresh, the panel re-queries all widgets at a configured interval (e.g. every five minutes). There is a small clock icon <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/23f1.png" alt="⏱" class="wp-smiley" style="height: 1em; max-height: 1em;" /> on the top-right corner of the Business Dashboard, which represent the Auto-refresh feature. When it&#8217;s off, the icon is grey, while it becomes green upon activation. Depending on configuration, you might be able (or not) to toggle the Auto-refresh.</li>



<li><strong>Global manual refresh.</strong> A small <strong><em>↻</em></strong> button in the top bar refreshes every widget at once. To avoid accidental hammering, the button enters a 15 second cooldown after a click. The per-widget refresh buttons are also locked during this cooldown.</li>



<li><strong>Per-widget refresh.</strong> Each widget has its own <strong><em>↻</em></strong> button in its top-right corner. Click it to refresh that widget alone. A 15 second cooldown applies to the clicked widget only.</li>
</ul>



<p class="wp-block-paragraph">Each widget header also shows a faint <strong>&#8220;Xs&#8221;</strong> or <strong>&#8220;Xm&#8221;</strong> or <strong>&#8220;Xh&#8221;</strong> text indicating when the data was last successfully loaded (in seconds, minutes or hours). Hover it to see the exact timestamp in a tooltip: <strong>&#8220;<em>Last refreshed: hh:mm:ss</em>&#8220;</strong>. This is useful to spot, at a glance, if all widgets were successfully refreshed or if there is one that is fresher than another (typically because you refreshed it manually).</p>



<h2 id="h-5-drill-through-exploring-the-objects-behind-the-numbers" class="wp-block-heading">5. Drill-through: exploring the objects behind the numbers</h2>



<p class="wp-block-paragraph">This is one of the features that makes the difference between an &#8220;interesting dashboard&#8221; and an &#8220;actually useful solution&#8221;. If your administrator enabled drill-through on the dashboard, all widgets automatically become clickable, and clicking them opens a <strong>detail modal</strong> listing the underlying M-Files objects.</p>



<p class="wp-block-paragraph">What is clickable:</p>



<figure class="wp-block-table"><table><thead><tr><th>Element</th><th>What it shows</th></tr></thead><tbody><tr><td>KPI tile</td><td>All the objects matching that widget&#8217;s query.</td></tr><tr><td>Gauge face</td><td>All the objects matching the widget&#8217;s query before applying the reducer (e.g. the minimum date is displayed, but all objects are shown in the drill-through).</td></tr><tr><td>Donut slice</td><td>Objects in that specific category or time period.</td></tr><tr><td>Bar column</td><td>Same as donut.</td></tr><tr><td>Line point</td><td>Same as donut.</td></tr><tr><td>Area point</td><td>Same as donut.</td></tr><tr><td>Row in a <strong><em>list</em></strong> table</td><td>No modal; clicking an object name navigates directly to it</td></tr><tr><td>Row in other tables</td><td>Same as donut.</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">The modal that opens shows a table with one column for the object name (as a clickable link &#8211; c.f. <strong><em>navigation</em></strong> section below), plus any additional columns your administrator configured. Columns are sortable: click a column header to sort ascending, click again for descending. Sorting applies across <strong>all fetched rows</strong>, not just the visible page. So if you sort an &#8220;Amount&#8221; column descending, you see the top 15 (that&#8217;s the default page size, but it is configurable, c.f. below with an example of 25) of all the rows that were retrieved, not just the page you happened to be on.</p>



<p class="wp-block-paragraph"><strong>Note:</strong> Sometimes the bar shows a small warning like <code>"(&#x26a0; 500 object(s) fetched)"</code>. This tells you the server retrieved up to its scan cap and there may be more objects in the vault that did not make it into the modal. Hover the <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> icon for an explanation. If you regularly hit that cap on a widget that should show everything, your administrator can raise (or disable) the cap on the dashboard configuration.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1219c5c5&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1219c5c5" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1766" height="1898" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.5-2.png" alt="A drill-through modal opened, showing the list of documents for a specific customer" class="wp-image-45125" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.5-2.png 1766w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.5-2-279x300.png 279w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.5-2-953x1024.png 953w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.5-2-768x825.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.5-2-1429x1536.png 1429w" sizes="auto, (max-width: 1766px) 100vw, 1766px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h3 id="h-why-drill-through-can-potentially-show-more-objects-than-the-widget-counts" class="wp-block-heading">Why drill-through can potentially show more objects than the widget counts</h3>



<p class="wp-block-paragraph">This deserves its own paragraph because it might be a bit counter-intuitive at first. The widget aggregation runs inside a capped scan (set by your administrator), while drill-through runs a focused search with the slice or row category as an additional filter. If the focused search can fit all matching objects under its own cap, you may see more objects in the modal than the widget counted.</p>



<p class="wp-block-paragraph">The <strong><em>Partial results</em></strong> badge on the widget header (when present) is the warning that the widget aggregate may be incomplete. There are basically two different limits. A first <strong>global</strong> limit, that ensures we do not request too much from the server and a second one linked to the <strong>drill-through</strong> that applies on filtered results.</p>



<p class="wp-block-paragraph"><strong><em>Example:</em></strong> A dashboard has been configured by your admin with 10&#8217;000 for the first limit and 5&#8217;000 for the second. Let&#8217;s take a <strong>Documents by Class</strong> donut widget (i.e. you want to see ALL documents &#8211; without any filters). If your vault has more than 10&#8217;000 objects of type <strong>Documents</strong>, then the widget would display 10&#8217;000 with a <strong><em>Partial results</em></strong> badge. In these first then thousand results, there might have been 150 <strong>Contract or Agreement</strong> (Class) shown in the donut. If you click on the donut slice for <strong>Contract or Agreement</strong>, the associated modal will open and it will show at least 150 and up to 5&#8217;000 results. In the end, if the initial search is <strong>&#8220;too wide&#8221;</strong>, you might hit a limit. But the drill-through will include another filter (here &#8220;Class=Contract or Agreement&#8221;), which only return these specific objects.</p>



<p class="wp-block-paragraph">Again, this is for performance reasons and as a kind of server-protection. If you have a real need, these limits can be increased or disabled completely by the admin &#8211; it is simply like that by default.</p>



<h2 id="h-6-navigating-to-an-object" class="wp-block-heading">6. Navigating to an object</h2>



<p class="wp-block-paragraph">In drill-through modals and in <strong><em>list</em></strong>-type table widgets, object names are blue underlined links. Clicking a link opens that object on the left-side of M-Files.</p>



<p class="wp-block-paragraph">A small detail that is sometimes useful: the drill-through modal <strong>stays open</strong> after you click a link, so you can navigate to several objects one after the other without losing your place in the list. Close the modal manually when you are done (using the <strong><em>×</em></strong> button, the Escape key, or clicking outside the modal).</p>



<figure class="wp-block-video"><video height="2150" style="aspect-ratio: 3478 / 2150;" width="3478" controls poster="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-7-scaled.png" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.6-2.mov"></video></figure>



<h2 id="h-7-exporting-to-pdf" class="wp-block-heading">7. Exporting to PDF</h2>



<p class="wp-block-paragraph">If your administrator enabled PDF export, a <strong><em>⎙ PDF</em></strong> button appears in the top bar of the dashboard. Clicking it generates a snapshot of the current dashboard state (charts as images, KPI numbers, tables) and <strong>opens a print dialog automatically</strong> (both on Web and Desktop). From there:</p>



<ul class="wp-block-list">
<li>Select &#8220;Save as PDF&#8221; (or &#8220;Microsoft Print to PDF&#8221;) as the printer and click Save.</li>



<li>Or pick a physical printer to print a hard copy.</li>
</ul>



<p class="wp-block-paragraph">The top bar, the refresh buttons and any open modals are excluded from the snapshot, so the PDF stays clean.</p>



<p class="wp-block-paragraph"><strong><em>Note 1:</em></strong> if your client blocks the automatic print dialog (a rare but possible restriction), an HTML file is downloaded instead. You can open that file in any browser and press Ctrl+P to print it in the exact same way.<br><strong><em>Note 2:</em></strong> the PDF export will respect exactly what M-Files is currently showing. This allows you to export the dashboard with a specific configuration. For example a table sorted in a different way that the default one, or some legends unselected from a donut, so that it ignores these details and re-render the donut with the remaining categories or time periods, etc. Whatever M-Files is showing will be what is present in the PDF export.</p>



<p class="wp-block-paragraph">A PDF export of a dashboard will look like that:</p>



<div data-wp-interactive="core/file" class="wp-block-file"><object data-wp-bind--hidden="!state.hasPdfPreview" hidden class="wp-block-file__embed" data="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/Contracts-Snapshot-01-Nov-2011.pdf" type="application/pdf" style="width:100%;height:600px" aria-label="Embed of Contracts Snapshot (01-Nov-2011)."></object><a id="wp-block-file--media-690988a8-2ead-4395-83b1-e4f1850ea708" href="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/Contracts-Snapshot-01-Nov-2011.pdf">Contracts Snapshot (01-Nov-2011)</a><a href="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/Contracts-Snapshot-01-Nov-2011.pdf" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-690988a8-2ead-4395-83b1-e4f1850ea708">Download</a></div>



<h2 id="h-8-exporting-to-csv" class="wp-block-heading">8. Exporting to CSV</h2>



<p class="wp-block-paragraph">Similarly to the PDF export, your administrator can also enable CSV export. When it is on, a <strong><em>⊞ CSV</em></strong> button appears in the header of every <strong><em>table</em></strong>, <strong><em>bar</em></strong>, <strong><em>donut</em></strong>, <strong><em>line</em></strong>, and <strong><em>area</em></strong> widget, as well as in the header of the drill-through modal for all widgets.</p>



<p class="wp-block-paragraph">Clicking <strong><em>⊞ CSV</em></strong> downloads a <strong><em>.csv</em></strong> file immediately. There is no extra server call; the export uses the data already in memory, so it is fast.</p>



<p class="wp-block-paragraph">The file starts with a small metadata block describing what the data represents / how it was fetched:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: xml; title: ; notranslate">
Widget Name,Active Contracts
Object Type,Document
Object Class,Contract or Agreement
Filter #1,Effective through greaterOrEqual @today
Aggregation Details,summary | count
Export Date,2026-06-04 19:30:08
</pre></div>


<p class="wp-block-paragraph">There might be two additional lines if the export is done on a specific group (a part of the initial results (e.g. after opening a donut&#8217;s slice modal details)) and if the widget is configured with multi-series. One line will describe which group is exported (category or time period) and another will describe the second dimension (the series).</p>



<p class="wp-block-paragraph">The above metadata section is followed by a blank separator row (empty line), then the actual data rows (column headers and associated values). All rows currently held in the browser are exported, not just the page you are looking at. For example, if the table shows page 2 only (objects 16-30) but 120 results were fetched, then all 120 rows will be in the CSV.</p>



<p class="wp-block-paragraph">The CSV file exported can then be opened in your preferred editor (Microsoft Excel, Euro-Office, LibreOffice, etc&#8230;). In the below image, I simply added the headers in Bold, but otherwise it&#8217;s the default outcome.</p>



<p class="wp-block-paragraph"><strong><em>Note:</em></strong> similarly to the PDF export, the CSV will also respect table sorting, so that the rows exported will be in the exact same order as the one that you see on your screen.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1219dc8f&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1219dc8f" class="wp-block-image size-full is-style-default wp-lightbox-container"><img loading="lazy" decoding="async" width="1306" height="1175" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.8-1.png" alt="CSV export for a Purchase Invoices widget, showing the list of Purchase Invoices by Workflow state." class="wp-image-44939" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.8-1.png 1306w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.8-1-300x270.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.8-1-1024x921.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.8-1-768x691.png 768w" sizes="auto, (max-width: 1306px) 100vw, 1306px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-9-multiple-favorite-dashboards" class="wp-block-heading">9. Multiple favorite dashboards</h2>



<p class="wp-block-paragraph">If you use some dashboards far more often than the others, you can pin them as your favorites. There is a small star button (<strong><em>☆</em></strong>) in the top bar:</p>



<ul class="wp-block-list">
<li>Select the dashboard you want to pin.</li>



<li>Click the <strong><em>☆</em></strong> button. It turns gold (<strong><em>★</em></strong>).</li>



<li>From now on, when you open the Dashboard pane, the first dashboard you set as favorite will load first (instead of the last you viewed), and you can access your favorites at the top of the dashboard dropdown.</li>
</ul>



<p class="wp-block-paragraph">To unpin, click the <strong><em>★</em></strong> again. The favorites are stored under your account, so it follows you across clients and devices. Do it once and you can then enjoy forever.</p>



<h2 id="h-10-sharing-a-dashboard-link" class="wp-block-heading">10. Sharing a dashboard link</h2>



<p class="wp-block-paragraph">Right next to the favorite button, a small link button (<strong><em><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f517.png" alt="🔗" class="wp-smiley" style="height: 1em; max-height: 1em;" /></em></strong>) can be used to share a link to a specific dashboard with someone else. In both <strong>M-Files Web</strong> and <strong>M-Files Desktop</strong>, the clipboard will be updated to contain a link to the current dashboard. For that purpose, the administrator will have to set the public facing URL of M-Files in the M-Files Admin configuration.</p>



<p class="wp-block-paragraph">If this public URL isn&#8217;t defined, then it will still work on M-Files Web, by automatically fetching the current URL and appending it with the reference to the current dashboard ID. However, on M-Files Desktop, the URL is actually a local one, which nobody else will be able to use and therefore, the fallback is to copy the dashboard Name instead.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab1219e3f5&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab1219e3f5" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1766" height="1086" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.10-4.png" alt="Image showing the favorite gold star as well as the copy link feature." class="wp-image-45128" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.10-4.png 1766w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.10-4-300x184.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.10-4-1024x630.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.10-4-768x472.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.10-4-1536x945.png 1536w" sizes="auto, (max-width: 1766px) 100vw, 1766px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-11-indicators-worth-knowing" class="wp-block-heading">11. Indicators worth knowing</h2>



<p class="wp-block-paragraph">A few small visual indicators are worth recognizing:</p>



<ul class="wp-block-list">
<li><strong><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Partial results</strong> (orange badge in the widget header). The server scan cap was reached; the widget may not reflect the full data. Section 5 above has the full explanation.</li>



<li><strong>Grey placeholder text</strong> inside a widget. The dashboard definition has a small mis-configuration. The administrator was normally warned about it when he designed the dashboard but he probably ignored it.</li>



<li><strong>Red error overlay</strong> that replaces a widget. The server returned an unexpected error. The dashboard definition has a configuration problem. The dashboard designer part (admin) doesn&#8217;t allow to Save/Publish dashboards with such errors, but someone might have bypassed the administrative tool and updated the database directly.</li>



<li><strong>Spinning overlay</strong>. The widget is loading. If it stays in that state for a long time, the vault may be under load. Use the per-widget refresh button to retry once the spinner disappears. I haven&#8217;t seen the spinner stays more than 2 seconds myself, but I guess it could happen.</li>



<li><strong>Numbers higher than what you see in your views</strong>. Dashboard widgets can use either user-level permissions (default) or server-level permissions, depending on the configuration done by the administrator. Some use-cases might require to give access to all M-Files users to some KPIs for example. In this case, a dashboard could use server-level permissions, so that the KPIs are accurate and the same for everybody, even people with no access to the underlying objects. The navigation (links) to the objects will always respect your actual user-level permissions, obviously. So the only thing that the dashboard can give you access to, if configured in that way by the administrator, is KPIs or some charts (possibly even without the drill-through &#8211; so you don&#8217;t have any details about the objects themselves).</li>
</ul>



<h2 id="h-12-what-comes-next" class="wp-block-heading">12. What comes next</h2>



<p class="wp-block-paragraph">What I believe is genuinely useful about this module, is that it removes the small friction of &#8220;I just want to know quickly&#8221;. Opening a Power BI report takes a context switch; opening a saved view, scrolling and counting takes time. The Business Dashboard sits one click away in the M-Files window the user is already in, and it shows the answer he is looking for with nice visuals.</p>



<p class="wp-block-paragraph">In the next post of this series, I will move to the other side of the panel: how an administrator configures a dashboard. I will start with the <strong>anatomy of a dashboard definition</strong> &#8211; the JSON structure that drives everything you just saw.</p>



<p class="wp-block-paragraph">Want to know more about this Business Dashboard? <a href="https://www.dbi-services.com/company/contact/" target="_blank" rel="noreferrer noopener">Contact us</a> and we will be happy to showcase it on <a href="https://www.m-files.com/" target="_blank" rel="noreferrer noopener">M-Files</a>.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-bd-end-user-experience/">M-Files BD &#8211; End-user experience</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/m-files-bd-end-user-experience/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.6-2.mov" length="30331080" type="video/quicktime" />

			</item>
		<item>
		<title>M-Files &#8211; Introducing the Business Dashboard module</title>
		<link>https://www.dbi-services.com/blog/m-files-introducing-the-business-dashboard-module/</link>
					<comments>https://www.dbi-services.com/blog/m-files-introducing-the-business-dashboard-module/#respond</comments>
		
		<dc:creator><![CDATA[Morgan Patou]]></dc:creator>
		<pubDate>Tue, 02 Jun 2026 19:50:32 +0000</pubDate>
				<category><![CDATA[Enterprise content management]]></category>
		<category><![CDATA[Business Dashboard]]></category>
		<category><![CDATA[Charts]]></category>
		<category><![CDATA[Gauge]]></category>
		<category><![CDATA[KPI]]></category>
		<category><![CDATA[M-Files]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=44913</guid>

					<description><![CDATA[<p>Some time ago, a customer asked whether it was possible, in M-Files, to have dashboards with KPIs and a global overview of what was happening in their vault. It was a topic I had in my radar for a few years already, but I never had &#8211; or took &#8211; the time to really work [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-introducing-the-business-dashboard-module/">M-Files &#8211; Introducing the Business Dashboard module</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Some time ago, a customer asked whether it was possible, in M-Files, to have dashboards with KPIs and a global overview of what was happening in their vault. It was a topic I had in my radar for a few years already, but I never had &#8211; or took &#8211; the time to really work on it. I jumped on the occasion to start building a solution in parallel of my daily-job at dbi services. Several evenings (who said nights?), weekends and some holidays later, here we are.</p>



<p class="wp-block-paragraph">The starting point was a real observation: the views of M-Files are powerful, the metadata model is flexible, the workflows are solid, but <strong>getting an at-a-glance picture of the business is still a clicking exercise</strong>. You open a view, you count, you open another view, you count again, you switch to the search, you start building an Excel with what you found&#8230; However, you might still not have the chart you wanted to present in the management meeting that starts in ten minutes.</p>



<p class="wp-block-paragraph">This blog post introduces what I called the <strong>Business Dashboard module</strong>, a custom M-Files extension designed and built to fill that gap. This is the first post of a series that will walk through the module. From the end-user experience, to the authoring side, and finally to a complete worked example. I will use the M-Files <strong>Sample Vault</strong> for this series since it contains test data. You can quickly and easily deploy that vault via the <a href="https://product.m-files.com/downloads/" target="_blank" rel="noreferrer noopener">M-Files installer</a>.</p>



<h2 id="h-1-the-problem-to-solve" class="wp-block-heading">1. The problem to solve</h2>



<p class="wp-block-paragraph">As you might know, M-Files ships with a few visualization features (views, search facets, the right-hand Metadata and Preview panes). With these, you can answer one of the following questions, but never all at once and never at a single glance:</p>



<ul class="wp-block-list">
<li>How many contracts are currently active in the vault?</li>



<li>How many of them expire in the next 30 days?</li>



<li>What is the distribution of our contracts by agreement type?</li>



<li>What is the total amount of invoices waiting to be approved, broken down by customer?</li>



<li>How many documents was each employee responsible for last quarter?</li>
</ul>



<p class="wp-block-paragraph">These are not exotic questions. They are the kind of things you might face every week. And the honest answer, today, is &#8220;I will get back to you later&#8221;. Sometimes there is a Power BI dashboard plugged into the vault SQL backend that answers some of them, but that is a second tool, a second login, often a second team to involve, and usually a permission/access story that does not match the per-user M-Files context.</p>



<p class="wp-block-paragraph">I wanted something simpler. Something that lives <strong>inside M-Files</strong>, that integrates with the M-Files access model, that an administrator can configure without writing any code, and that the end user can open with one click from within M-Files.</p>



<h2 id="h-2-what-the-business-dashboard-module-is" class="wp-block-heading">2. What the Business Dashboard module is</h2>



<p class="wp-block-paragraph">The Business Dashboard is an additional <strong>panel</strong> that appears on the right-hand side of M-Files (alongside Metadata and Preview). It works in both M-Files Web and Desktop (latest UI). End users pick a dashboard to render, which contains <strong>widgets</strong> (tiles) that display live business data from the vault using KPIs, donut, bar, line, area, tables or gauges. Data is fetched live from M-Files and it can refresh itself. It provides multiple other features such as export to PDF or CSV, favorites, links, even object navigation, etc&#8230; From a security point of view, the access control will be in <a href="https://www.dbi-services.com/blog/m-files-bd-the-admin-part/" id="45312" target="_blank" rel="noreferrer noopener">Post 7</a>.</p>



<p class="wp-block-paragraph">For administrators, dashboards are defined as <strong>JSON documents</strong> edited inside a dedicated tab in M-Files Admin. The editor offers both a JSON editor and a <strong>Visual Designer</strong> that lets you build dashboards without any knowledge of what JSON is. Definitions are stored in the vault, so a backup includes them automatically and there is no extra database to provision or manage.</p>



<p class="wp-block-paragraph">Two design choices are worth pulling out here, because they shape everything else in the series:</p>



<ul class="wp-block-list">
<li><strong>The engine is fully generic.</strong> There is no hardcoded business concept anywhere in the code. The same module works for contracts, invoices, customers, employees, projects, or whatever objects is contained within your vault. The JSON tells the engine <strong>how to query</strong> (which object type, which class, which property to group by, which filter to apply), not <strong>what values to expect</strong>.</li>



<li><strong>The wire format is frozen.</strong> Once the dashboard JSON is saved, the calls between the UI and the server speak a fixed shape. This means that when/if I ship a new widget type or a new aggregation, existing dashboards will keep working with no migration.</li>
</ul>



<p class="wp-block-paragraph">This is what the <strong>Business Dashboard</strong> looks like, from end-user point of view (click to enlarge the image):</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6a4ab121a8b24&quot;}" data-wp-interactive="core/image" data-wp-key="6a4ab121a8b24" class="wp-block-image size-full is-style-default wp-lightbox-container"><img loading="lazy" decoding="async" width="2560" height="2059" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-5-scaled.png" alt="M-Files Business Dashboard" class="wp-image-45119" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-5-scaled.png 2560w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-5-300x241.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-5-1024x823.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-5-768x618.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-5-1536x1235.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/2.1-5-2048x1647.png 2048w" sizes="auto, (max-width: 2560px) 100vw, 2560px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 id="h-3-concrete-use-cases" class="wp-block-heading">3. Concrete use cases</h2>



<p class="wp-block-paragraph">Rather than list features, let me describe four concrete scenarios where the <strong>Business Dashboard</strong> could shine:</p>



<ul class="wp-block-list">
<li><strong>Contracts overview.</strong> A KPI tile showing the number of active contracts, another showing those expiring in the next 30 days, a donut chart by agreement type, a bar chart showing expiries per month for the next year (e.g. until end of year or next 365 days), and a list of contracts to review this week. One panel, no clicking, refreshed automatically.</li>



<li><strong>Purchase invoices.</strong> Total amount waiting for approval, oldest invoice still pending, distribution by customer/countries/regions, a gauge of overdue invoices against a target of zero.</li>



<li><strong>Employees and responsibilities.</strong> A list of employees by department, with the count of documents worked on by each one in the past week. Possibly the list of people in holidays with their substitute. Useful for capacity planning and for the &#8220;who is on vacation, who covers what&#8221; conversation.</li>



<li><strong>Customer activity.</strong> Documents by customer in the last quarter, broken down by document class. Quickly spot the customers who went quiet and the ones who suddenly became very active.</li>
</ul>



<p class="wp-block-paragraph">None of these are hardcoded in the module. They are <strong>examples of what an administrator can configure</strong>, once, using the Visual Designer or the JSON editor. The same engine produces all of them and any number of variations.</p>



<p class="wp-block-paragraph"><strong>Note:</strong> Whatever the widget used, with at most one more click, you can get the details of which objects correspond to what you are looking for. For example, if you have a KPI for the number of active contracts, if it shows 25, then simply clicking on that 25 will open a <strong>drill-through</strong> with the list of all 25 objects in that specific situation, with optional additional details. See the follow-up posts for more details on that (and all other features).</p>



<h2 id="h-4-where-it-runs" class="wp-block-heading">4. Where it runs</h2>



<p class="wp-block-paragraph">The module contains two parts:</p>



<ul class="wp-block-list">
<li><strong><em>BusinessDashboard</em></strong> &#8211; the server-side, which contains the query engine, the validator, the persistence layer, etc.</li>



<li><strong><em>BusinessDashboard.UIX</em></strong> &#8211; the client-side, which contains all the display part for the end-users.</li>
</ul>



<p class="wp-block-paragraph">Both are installed, independently, in the M-Files Admin, via the standard application installation process. In regards to compatibility, it should technically supports all recent versions of M-Files that uses the new UI. I personally tested it extensively on M-Files 26.3 / 26.4 / 26.5.</p>



<p class="wp-block-paragraph">No external service is contacted; everything runs inside the vault. This makes the module a good fit for both on-premises and cloud deployments (though for cloud, the module would need to first be validated by M-Files), or for any deployment where adding a second tool (Power BI, Grafana) is not desirable (too complex to setup, too much for the actual need, or simply for security reasons).</p>



<h2 id="h-5-what-this-module-is-not" class="wp-block-heading">5. What this module is not</h2>



<p class="wp-block-paragraph">I think it is fair, before closing this introduction, to flag what the Business Dashboard module is <strong>not</strong> trying to do.</p>



<ul class="wp-block-list">
<li>It is <strong>not a BI tool</strong>. No SQL access, no data warehouse, no cross-vault joins. The engine only queries the M-Files vault it is deployed on and that&#8217;s it. If you need to combine data from a vault and an ERP, a dedicated BI stack might still be the right answer. <strong>UNLESS</strong> you are already merging the ERP data in M-Files (external repository or database connector). In that case, the Business Dashboard would be able to query it as well.</li>



<li>It is <strong>not (really) a reporting engine</strong>. No scheduled report, no email delivery. However, you can export a dashboard as PDF and you can export a widget (or its drill-through) to CSV (Excel-compatible).</li>



<li>It is <strong>not (really) designed for very large aggregations</strong>. The engine fetches up to a configurable amount of objects per widget (500 by default &#8211; can be increased, decreased or disabled completely&#8230;). Then aggregates it in memory, which is fine for any reasonable business question. Though it is not fine for things such as &#8220;list all 50 million objects that were created in the last 10 years&#8221;&#8230; If the limit is hit, the user knows about it with the help of a special badge.</li>



<li>It is <strong>not a replacement for M-Files views</strong> (or is it&#8230;?). Depending on use-cases, views can still be a better starting point. The Business Dashboard sits next to them, not in front of them.</li>
</ul>



<p class="wp-block-paragraph">If your need fits inside those boundaries, the module might make your life easier, irrespective of whether you are a decision maker, a power user, a simple user or an administrator. If it does not, please get in touch and I will be happy to discuss what would!</p>



<h2 id="h-6-what-comes-next-in-the-series" class="wp-block-heading">6. What comes next in the series</h2>



<p class="wp-block-paragraph">This series is going to cover, in order:</p>



<ul class="wp-block-list">
<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-end-user-experience/" id="44932" target="_blank" rel="noreferrer noopener">Post 2</a></strong> &#8211; The <strong>end-user experience</strong>: opening the panel, switching dashboards, drill-through, exports, favorites, sharing.</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-anatomy-of-a-dashboard-definition/" id="44952" target="_blank" rel="noreferrer noopener">Post 3</a></strong> &#8211; The anatomy of a <strong>dashboard definition</strong>: the JSON structure that drives everything.</li>



<li><strong>Posts <a href="https://www.dbi-services.com/blog/m-files-bd-scalar-widgets-kpinumber-and-gauge/" id="45018" target="_blank" rel="noreferrer noopener">4a</a>, <a href="https://www.dbi-services.com/blog/m-files-bd-trend-widgets-line-and-area/" id="45076" target="_blank" rel="noreferrer noopener">4b</a>, <a href="https://www.dbi-services.com/blog/m-files-bd-distribution-and-tabular-widgets-donut-bar-table/" id="45109" target="_blank" rel="noreferrer noopener">4c</a></strong> &#8211; The <strong>widget catalog</strong>, split by type family: scalar widgets (<strong><em>kpiNumber</em></strong>, <strong><em>gauge</em></strong>), trend widgets (<strong><em>line</em></strong>, <strong><em>area</em></strong>), and distribution / tabular widgets (<strong><em>donut</em></strong>, <strong><em>bar</em></strong>, <strong><em>table</em></strong>).</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-queries-objecttype-class-filters-date-tokens/" id="45175" target="_blank" rel="noreferrer noopener">Post 5</a></strong> &#8211; Writing queries: <strong><em>objectType</em></strong>, <strong><em>class</em></strong>, <strong>filters</strong>, date tokens.</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-aggregations-reducers-and-series/" id="45206" target="_blank" rel="noreferrer noopener">Post 6</a></strong> &#8211; <strong>Aggregations</strong> and <strong>reducers</strong>.</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-the-admin-part/" id="45312" target="_blank" rel="noreferrer noopener">Post 7</a></strong> &#8211; The <strong>Admin tab</strong>: actions, Visual Designer / JSON editor, validation, import / export, access control.</li>



<li><strong><a href="https://www.dbi-services.com/blog/m-files-bd-putting-it-together-with-the-visual-designer/" id="45433" target="_blank" rel="noreferrer noopener">Post 8</a></strong> &#8211; Building a dashboard with the <strong>Visual Designer</strong>.</li>
</ul>



<p class="wp-block-paragraph">If you have a specific question or a use case you would like to see covered in the series, don&#8217;t hesitate to reach out, and I might be able to add it to my to-do list.</p>



<p class="wp-block-paragraph">In the meantime, the next post will be a tour of what an end user sees the first time they click on the new Dashboard tab. See you there.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/m-files-introducing-the-business-dashboard-module/">M-Files &#8211; Introducing the Business Dashboard module</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/m-files-introducing-the-business-dashboard-module/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 
Lazy Loading (feed)

Served from: www.dbi-services.com @ 2026-07-05 21:31:45 by W3 Total Cache
-->