Source: products/argos-saleslogix/src/Views/MetricWidget.js

import declare from 'dojo/_base/declare';
import lang from 'dojo/_base/lang';
import Deferred from 'dojo/Deferred';
import when from 'dojo/when';
import _Widget from 'dijit/_Widget';
import _Templated from 'argos/_Templated';
import SDataStore from 'argos/Store/SData';
import getResource from 'argos/I18n';
import format from 'crm/Format';
import aggregate from 'crm/Aggregate';


const resource = getResource('metricWidget');

/**
 * @class crm.Views.MetricWidget
 */
const __class = declare('crm.Views.MetricWidget', [_Widget, _Templated], /** @lends crm.Views.MetricWidget# */{
  /**
   * @property {Simplate}
   * Simple that defines the HTML Markup
   */
  widgetTemplate: new Simplate([
    '<div class="metric-widget">',
    '<button data-dojo-attach-event="onclick:navToReportView" {% if (!$.chartType) { %} disabled {% } %}>',
    '<div data-dojo-attach-point="metricDetailNode" class="metric-detail">',
    '{%! $.loadingTemplate %}',
    '</div>',
    '</button>',
    '</div>',
  ]),

  /**
   * @property {Simplate}
   * HTML markup for the metric detail (name/value)
   */
  itemTemplate: new Simplate([
    '<h1 class="metric-value">{%: $$.formatter($.value) %}</h1>',
    '<span class="metric-title">{%: $$.title %}</span>',
  ]),

  /**
   * @property {Simplate}
   */
  errorTemplate: new Simplate([
    '<div class="metric-title">{%: $$.errorText %}</div>',
  ]),

  /**
   * @property {Simplate}
   * HTML markup for the loading text and icon
   */
  loadingTemplate: new Simplate([
    '<div class="metric-title list-loading busy-sm">',
    '<span class="list-loading-indicator">',
    '<div class="busy-indicator-container" aria-live="polite">',
    '<div class="busy-indicator active">',
    '<div class="bar one"></div>',
    '<div class="bar two"></div>',
    '<div class="bar three"></div>',
    '<div class="bar four"></div>',
    '<div class="bar five"></div>',
    '</div>',
    '<span>{%: $.loadingText %}</span>',
    '</div>',
    '</span>',
    '</div>',
  ]),

  // Localization
  title: '',
  loadingText: resource.loadingText,
  errorText: resource.errorText,

  // Store Options
  querySelect: null,
  queryName: null,
  queryArgs: null,
  queryOrderBy: null,
  resourceKind: null,
  resourcePredicate: null,
  contractName: null,
  keyProperty: null,
  applicationName: null,
  position: 0,
  pageSize: 100,

  store: null,

  _data: null,
  value: null,
  requestDataDeferred: null,
  metricDetailNode: null,
  currentSearchExpression: '',

  // Chart Properties
  chartType: null,
  chartTypeMapping: {
    pie: 'chart_generic_pie',
    bar: 'chart_generic_bar',
    line: 'chart_generic_line',
  },

  formatModule: format,
  formatter: format.bigNumber,

  /**
   * Calculates the value shown in the metric widget button.
   * @param {Array} data Array of data used for the metric
   * @return {int} Returns a value calculated from data (SUM/AVG/MAX/MIN/Whatever)
   */
  valueFn: function valueFn(data = []) {
    return data.reduce((p, c) => p + c.value, 0);
  },

  // Functions can't be stored in localstorage, save the module/fn strings and load them later via AMD
  aggregateModule: aggregate,
  aggregate: null,

  /**
   * Requests the widget's data, value fn, format fn, and renders it's itemTemplate
   */
  requestData: function requestData() {
    this.inherited(arguments);

    if (this._data && this._data.length > 0) {
      return;
    }

    this._data = [];
    this.requestDataDeferred = new Deferred();
    this._getData();

    this.requestDataDeferred
      .then((results) => {
        const data = results;
        if (!data) {
          throw new Error('An error occurred loading the KPI widget data.');
        }

        this.valueFn = this.aggregateModule[this.aggregate];
        this.formatter = this.formatModule[this.formatter];

        const value = this.value = this.valueFn.call(this, data);
        $(this.metricDetailNode).replaceWith(this.itemTemplate.apply({
          value,
        }, this));
      }, (err) => {
        // Error
        console.error(err); // eslint-disable-line
        $(this.metricDetailNode).replaceWith(this.errorTemplate.apply({}, this));
      });
  },
  navToReportView: function navToReportView() {
    if (!this.chartType) {
      return;
    }

    const view = App.getView(this.chartTypeMapping[this.chartType]);

    if (view) {
      view.parent = this;
      view.formatter = this.formatter;
      view.show({
        returnTo: this.returnToId,
        currentSearchExpression: this.currentSearchExpression,
        title: this.title,
      });
    }
  },
  _buildQueryOptions: function _buildQueryOptions() {
    return {
      count: this.pageSize,
      start: this.position,
    };
  },
  _buildQueryExpression: function _buildQueryExpression() {
    return null;
  },
  _getData: function _getData() {
    const queryOptions = this._buildQueryOptions();
    const queryExpression = this._buildQueryExpression();
    const store = this.get('store');
    const queryResults = store.query(queryExpression, queryOptions);
    when(queryResults, lang.hitch(this, this._onQuerySuccess, queryResults), lang.hitch(this, this._onQueryError));
  },
  _onQuerySuccess: function _onQuerySuccess(queryResults) {
    when(queryResults.total, (total) => {
      queryResults.forEach(lang.hitch(this, this._processItem));

      let left = -1;
      if (total > -1) {
        left = total - (this.position + this.pageSize);
      }

      if (left > 0) {
        this.position = this.position + this.pageSize;
        this._getData();
      } else {
        // Signal complete
        this.requestDataDeferred.resolve(this._data);
      }
    });
  },
  _processItem: function _processItem(item) {
    this._data.push(item);
  },
  _onQueryError: function _onQueryError(error) {
    this.requestDataDeferred.reject(error);
  },
  createStore: function createStore() {
    const store = new SDataStore({
      request: this.request,
      service: App.services.crm,
      resourceKind: this.resourceKind,
      resourcePredicate: this.resourcePredicate,
      contractName: this.contractName,
      select: this.querySelect,
      queryName: this.queryName,
      queryArgs: this.queryArgs,
      orderBy: this.queryOrderBy,
      idProperty: this.keyProperty,
      applicationName: this.applicationName,
      scope: this,
    });

    return store;
  },
  _getStoreAttr: function _getStoreAttr() {
    return this.store || (this.store = this.createStore());
  },
});

lang.setObject('Mobile.SalesLogix.Views.MetricWidget', __class);
export default __class;