Multiple Suppliers - UI Extension Points

From the pervious discussion of extending OpenLMIS to allow multiple suppliers and workflows related to requisition splitting — we have identified multiple requirements that the UI could support. This document outlines these additional requirements, and attempts to prioritize them in terms of level of effort required to implement these requirements.

CSS and SASS

There should be no special method of removing or explicitly overriding CSS — an implementer should simply use more specific CSS rules to change styles. This means that CSS styles in OpenLMIS should strive to be as general (shallow) as possible so that overriding a style can be done by creating a new CSS style. Some basic rules for doing are:

  • avoid the !important statement
  • use child selectors >
  • make mark-up semantic and class free (ie avoid divs)

All colors, absolute spacing, and browser breakpoints should be implemented using SASS variables, so that the overall UI look and feel can be changed by creating a different *.variables.scss file that will overwrite the initial declarations in OpenLMIS.

Generic Screens (Administration Screens)

When creating new functionality, there are needs to support the addition of configuration screens so configurations can be changed after initial implementation by a non-technical user. This set of extensions might not be used often by administrations, but for implementers there is value and re-assurance that these screens are available and accessible.

Creating new screens (easy & done)

Goal: Add new functionality to the OpenLMIS-UI

Examples:

  • Add new top-level functionality to OpenLMIS
  • Add configuration page to administration section
  • Add tab to requisition view page

The infrastructure to create new administration screens already exists - meaning an administrative user can navigate to a new screen that is defined in a UI docker module.

Enabling features:

  • UI-Router: Allows for addition of screens with a unique URL
  • NavigationService: Allows UI-Router definitions to expose their menu items into existing navigation frameworks
// Consider adding a view to see requisitions on a map
angular.module('custom-module').config(function($stateProvider){
  $stateProvider.state('requisitions.map', {
    url: '/map',
    showInNavigation: true,
    controller: 'MyCustomMapController',
    templateUrl: 'map/page.html'
  });
});

Replacing an existing screen (could be easy & not done)

Goal: Add content and functionality to an existing screen

Examples:

  • An implementation needs to replace the homepage with an interactive dashboard
  • A footer needs to be customized (less complicated than previous option)

Options:

Build Process Option - configure the build process so that files on a similar path overwrite eachother before the UI build command is run (this would mean creating a large temporary directory). The nice thing is that if you wanted to replace the home page template, all you would have to do it place a file at `src/home/page.html` (for example). The work involved here would be defined in the NodeJS dev tools, which are shared across all repositories.

UI-Router Solution - Rewrite a UI-Router route to use the newly created view template or controller. The implementation of this in AngularJS would require a run statement to make the switch, because you would need to wait for the original state to be registered. Problem: Not clear how errors would be handled.

angular.module('custom-module').run(function($state){
  var state = $state.get('home'); // get the current home page state
  state.view.templateUrl = 'new_tempalte.html';
  // gotta figure out how to change this...
});


Custom UI-Router 'Override' -
 Allow implementers to register a route with the property 'override,' which we would recognize in UI-Router and then use the last route with a specific name if the state has the property override. This would allow us to use the error handling framework already developed in UI-Router.

Appending form elements into existing screens w/o forking (hard)

Goal: To adding a single element or field into an existing screen without forking or replacing the entire screen.

The resulting code MUST be simpler than replacing the entire screen

Extendable forms, being able to inherit form pieces

Generally people want to extend the UI in unpredictable ways, and if you give them a bit of flexibility, they will always want more

Examples:

  • The v2 configure facility screen, which contains fieldsets of various different types, one could imagine adding a form or fieldset into the group
  • Requisition form actions, where an implementer might need to add a button

Options:

Ideal Solution: Develop an Extendable Service


Create an abstraction service where new UI elements could register HTML templates to. This service would work much like UI-Router and allow a person to add a template and controller to the current scope. We could generically apply this code to:

  • Any UI-Router route (if user specifies a specific view)
  • Any form element with a unique ID
  • Any element with a directive and unique ID (ie  <div extendable="foo.bar.1" />)


angular.module('custom-module').run(function($rootScope){
  $rootScope.$on('view:requisition.actions', function(templateList, scope, additionalArguments){
    // check scope variables, and do things
    templateList.push('custom-module/template.html');
  }
});


// if we wrap this implementation into a service/factory
angular.module('custom-module').run(function(uiViewExtender){
  uiViewExtender.add(
    'requisitions.requisition', // view name, could take patterns eventually maybe
    'form-actions', // view name to replace
    'custom-module/template.html', // template to add
    10 // priority of where to load (default would be 10, maybe)
    );
  }
});

Extending Business Logic

We would want to add Javascript events so new logic can be injected in various places, the problem is there are no natural places to inject this behavior, and implementers would need to define specific places for injection.

  • Where would add the logic for a button injected by a template?
  • Change how stock on hand is calculated (document this)
  • Are there easy extension points for service (adding new functionality and replacing the original provider — think angular.merge/extend)

Generic Search Result (or list) Screens

Most of the same techniques apply to changing a search results screen, and would a search result or list page.

The main problem with these screens is that they are currently written as a single page with no explicit points for extension, and its unclear what scenarios would drive changing these screens. Also the effort involved in replacing a search result screen entirely might be similar to the effort of extending the screen (as the OpenLMIS server endpoint would be the same, and does most of the lifting).

Extending search form

Goal: Add a new checkbox or variable to the existing search form, so that it is passed to the OpenLMIS server which was also extended


Scenario:
  • An implementation requires that products are searched by color
    See earlier note about extending a view, or extending a form element. The real trick here will be having a form submit event correctly pass the data through to the OpenLMIS server endpoint.

Replacing result template

This changes depending on how we implement lists, currently loops are evaluated in context, so there is no easy place to add additional code. We could create a structure to formalize item results using templates — but if new columns are added in a non-uniform fashion, we are going to have problems making sure rows and table columns match.

Modifying results shown

This is perhaps best done in a Service API that responds to the OpenLMIS-UI — as there are no existing hooks. It could be possible for services to create filter lists for transforming results, and create a simplistic event structure for acting on this data — but its messy at best.

Custom Screens with complex layouts (think Requisition Screen)

Places to add extension that shouldn't be a too hard

  • Fork requisition view template (overload with a method above) — there isn't much direct code that belongs to the view, and is mostly just structure. Forking this screen wouldn't be a big issue
  • Add view tabs to requisition view (probably with ui-router view) totally supported, allows for additional configuration.

Form Action Button Area

This is still an issue — the ideal solution would allow implementers to add their own custom buttons to a view

Additional Table Columns and functionality

There is a need to add table columns. The good news here is that if columns are added in the RequisitionService (backend) they will be displayed in the full-supply product view with no effort as that screen is already template driven.



SIL/SELV UI Changes to OpenLMIS (from Pawel)

The following are examples of changes to OpenLMIS that SolDevlo made to SIL/SELV

  • Localizable calendar — localaizable in all form components (people will find un-localizable parts)
  • Adding questions to columns — andding new rows and columns
  • Making sure different screen sizes and layouts work — not scroll but "wrap"
  • Being able to edit a distribution (how would you change the editablility)
  • Adding edit history screen
  • Need simplistic options to either add a button or replace a screen entirely

OpenLMIS: the global initiative for powerful LMIS software