Proposal: Build a dashboard framework to enable implementers to configure dashboards dynamically.
Context: We are starting an engagement with a client who is already using OpenMRS. One of their requirement is to be able to see a dashboard for each role. Based on the role of the user logged-in, there will be different widgets on the dashboard. As of now, the requirement is to have 3 types of widgets on the dashboard:
ToDo Dashboard Widget: Show the Tasks need to be performed by the user.
Appointments Widget: To be able to view a list of appointments for today.
Charting and Reporting Widgets: To be able to view Charts and Reports for specific KPIs.
We feel this might be a good opportunity to engage with the community and build a Generic Dashboard Engine which can be used to configure different type of dashboards(Patient Dashboard, User Dashboard, Role Based Dashboard) according to the need. The engine will be able to dynamically load different widgets based on the configuration. This will provide the flexibility to add a widget based on need with less/no code changes.
We have attached a concept note for the same and would like to hear the thoughts from community about it. Please feel free to share your opinions.Concept Note_ Dashboard Framework for OpenMRS.pdf (116.9 KB)
I am assuming that sections are for grouping controls/widgets together? Would they have a title?
For now, sections will contain list of widgets that should be shown in the dashboard. We do not have internal grouping for now.
How would you determine, which control is displayed where within a section (e.g. column)?
We want to start with simple. So we will use displayOrder to decide where to show the section (This will help us to achieve responsiveness easily). I guess we could change once after the designs been frozen.
Within microfrontends, there are “modules” and “exports for those modules,” which are what I’d recommend using for loading the correct module:
@sukreet I see most of these ideas are brand new - any chance of leveraging the ideas from the existing clinician facing dashboard, dashboard widgets and app configuration such as
That way you are not building from scratch and not re-inventing the wheel with things like configurations, privileges, age of observations to show, limits, etc
@joeldenning are the micro-front ends going to do away with the current OpenMRS referencing model of module-provider:fragment which is a relative path without having to specify absolute paths as in this example
Yes, fragments and module extensions are for server rendered java pages, not for client rendered JavaScript. The microfrontends code is all client rendered JavaScript (SPA). The older, existing ways of doing module extensions will continue to work for those using jsp.
@joeldenning , can there be a way, that the person defining the dashboard, does not really need to know where the widgets are loaded from? and technicalities of the widgets? If there was a visual editor, probably I would have a different view, but considering that these would be hand-written, can the definition be simpler.
What would be great if we can define in the dashboard json - what widget type are displayed, where and with what configuration/properties. Based on the type, if the framework can identify what widget to load (and from where), and pass on the defined properties to the widget and the widget can render whatever way it likes.
Now this would mean that the esm modules (specified in import-map) would somehow have to register themselves to a “directory” or sorts or for the framework to query modules to identify whether they are of certain type.
can there be a way, that the person defining the dashboard, does not really need to know where the widgets are loaded from?
Bahmni could put all the widgets into the same repo, and then the code wouldn’t need to be told which module they are in. This would probably work well for you.
For the main OpenMRS patient chart, we’re likely going to require the moduleName, since not all the main patient chart widgets across all distributions will be in the same git repo / in-browser module. However, that’s a separate project from the Bahmni project so they don’t have to solve the problem in the same way.
I was not referring to Bahmni as such. (even in Bahmni, we have different repos). The dashboard in question - is not for Bahmni!
but in Bahmni, we have a form engine - (Forms 2.0) - where widgets can be loaded from different bundles. What we expect is that widgets register with a “directory” for what type/sorts they can render. Same - for design time (we have WYSIWYG editor) - widgets describe themselves and register their designers controls.
Once done, when the definition is loaded, the form renderer just goes through the JSON definition - looks up the widgets and invokes the widget with the properties from the definition.
Bahmni’s dashboard works the same way - although for external widgets someone needs to tell where to fine the source code - but thats one time definition in a file - not for each dashboard widget.
What I am saying is that shouldn’t the new “client side way” learn and build upon the ways of the old JSP/GSP framework, leveraging the same conventions - that way the learning curve and 13 years of knowledge are not thrown away
Thanks @ssmusoke for your response. The reason for most of the naming convention changes are because of some or not relevant for the current implementation (like id, description) and some are more technical like instananceOf. So we wanted the naming convention to be more readable for none/less technical person too. We can change some of the key names like properties to config if we are good with that. Please provide your thoughts on this.
id - as there may be multiple instances of the same widget with different configurations, also use it to replace extensions.id and extensions.appId
instanceOf - type which can also replace the config.widget variable too
description is necessary so that a user can understand what the contents are
title/label: move into this level as this is what is displayed
order: keep for sorting and ordering (the higher the number the lower the widget is placed)
icon: move to top level too
extensions: this is where the widget is placed
extensionPointId - this is a very powerful feature as it provides where the widget will be placed, I recommend you leverage this approach
extensionParams - rename to source - of the widget, you can use module instead of provider, and widget name instead of fragment so that you do not have to use the full path (this requires a registry in place)
require: important for filtering to where it is displayed and shown
attn: @bistenes - this maybe your first case of config
Id - not needed. Even if within the same dashboard config, the same widget is used - the configs would be different. example: a widget chart working on two different dataset.
instanceof - no inference by type. so not applicable I would think.
order - I think “displayOrder” would be more appropriate if the team uses an “order” field for placement. IMHO, its too simplistic - but I will let the team decide depending on how they are solutioning placement of widgets.
regarding “config”, it would be always specific to the widget - so I would let the implementer decide. As long as they follow the process of documenting inline of what Brandon is suggesting in MF Squad. Regarding “extensions” - I think its better to let things evolve, rather than fix up definition upfront. I do think extensions are really powerful - but I think we need more discussions.