Source: products/argos-saleslogix/src/Views/Activity/MyList.js

import declare from 'dojo/_base/declare';
import lang from 'dojo/_base/lang';
import connect from 'dojo/_base/connect';
import environment from '../../Environment';
import ActivityList from './List';
import convert from 'argos/Convert';
import ErrorManager from 'argos/ErrorManager';
import action from '../../Action';
import format from '../../Format';
import _ListOfflineMixin from 'argos/Offline/_ListOfflineMixin';
import MODEL_TYPES from 'argos/Models/Types';
import MODEL_NAMES from '../../Models/Names';
import ActivityTypeText from '../../Models/Activity/ActivityTypeText';
import getResource from 'argos/I18n';
import string from 'dojo/string';


const resource = getResource('activityMyList');
const hashTagResource = getResource('activityMyListHashTags');

/**
 * @class crm.Views.Activity.MyList
 *
 * @extends crm.Views.Activity.List
 * @mixins crm.Views.Activity.List
 *
 * @requires argos.List
 * @requires argos.Format
 * @requires argos.Utility
 * @requires argos.Convert
 * @requires argos.ErrorManager
 *
 * @requires crm.Format
 * @requires crm.Environment
 * @requires crm.Views.Activity.List
 * @requires crm.Action
 *
 * @requires moment
 *
 */
const __class = declare('crm.Views.Activity.MyList', [ActivityList, _ListOfflineMixin], {
  format,
  // Templates
  // Card View
  rowTemplate: new Simplate([
    `<div data-action="activateEntry" data-my-activity-key="{%= $.$key %}" data-key="{%= $$.getItemActionKey($) %}" data-descriptor="{%: $$.getItemDescriptor($) %}" data-activity-type="{%: $.Activity.Type %}">
      <div class="widget">
        <div class="widget-header">
          {%! $$.itemIconTemplate %}<h2 class="widget-title">{%: $$.getItemDescriptor($) %}</h2>
          {% if($$.visibleActions.length > 0 && $$.enableActions) { %}
            <button class="btn-actions" type="button" data-action="selectEntry" data-key="{%= $$.getItemActionKey($) %}">
              <span class="audible">Actions</span>
              <svg class="icon" focusable="false" aria-hidden="true" role="presentation">
                <use xlink:href="#icon-more"></use>
              </svg>
            </button>
            {%! $$.listActionTemplate %}
          {% } %}
        </div>
        <div class="card-content">
          {%! $$.itemRowContentTemplate %}
        </div>
      </div>
    </div>`,
  ]),
  activityTimeTemplate: new Simplate([
    '{% if ($$.isTimelessToday($)) { %}',
    '{%: $$.allDayText %}',
    '{% } else { %}',
    '{%: $$.format.relativeDate($.Activity.StartDate, argos.Convert.toBoolean($.Activity.Timeless)) %}', // TODO: Avoid global
    '{% } %}',
  ]),
  itemTemplate: new Simplate([
    '<p class="listview-heading">',
    '<span class="p-description">{%: $$.format.picklist($$.app.picklistService, null, null, $$.getPicklistByActivityType($.Activity.Type, "Description"))($.Activity.Description) %}{% if ($.Status === "asUnconfirmed") { %} ({%: $$.format.userActivityStatus($.Status) %}) {% } %}</span>',
    '</p>',
    '<p class="micro-text">',
    '{%! $$.activityTimeTemplate %}',
    '</p>',
    '<p class="micro-text">{%! $$.nameTemplate %}</p>',
    '<p class="micro-text">',
    '{% if ($.Activity.PhoneNumber) { %}',
    '<span class="hyperlink" data-action="_callPhone" data-key="{%: $.$key %}">{%: argos.Format.phone($.Activity.PhoneNumber) %}</span>', // TODO: Avoid global
    '{% } %}',
    '</p>',
  ]),
  nameTemplate: new Simplate([
    '{% if ($.Activity.ContactName) { %}',
    '{%: $.Activity.ContactName %} | {%: $.Activity.AccountName %}',
    '{% } else if ($.Activity.AccountName) { %}',
    '{%: $.Activity.AccountName %}',
    '{% } else { %}',
    '{%: $.Activity.LeadName %}',
    '{% } %}',
  ]),

  // Localization
  titleText: resource.titleText,
  completeActivityText: resource.completeActivityText,
  acceptActivityText: resource.acceptActivityText,
  declineActivityText: resource.declineActivityText,
  callText: resource.callText,
  calledText: resource.calledText,
  addAttachmentActionText: resource.addAttachmentActionText,
  viewContactActionText: resource.viewContactActionText,
  viewAccountActionText: resource.viewAccountActionText,
  viewOpportunityActionText: resource.viewOpportunityActionText,

  // View Properties
  id: 'myactivity_list',
  entityName: 'UserActivity',
  modelName: MODEL_NAMES.USERACTIVITY,
  enableOffline: true,
  historyEditView: 'history_edit',
  existsRE: /^[\w]{12}$/,
  queryWhere: function queryWhere() {
    return `User.Id eq "${App.context.user.$key}" and Status ne "asDeclned" and Activity.Type ne "atLiterature"`;
  },
  queryOrderBy: 'Activity.StartDate desc',
  querySelect: [
    'Alarm',
    'AlarmTime',
    'Status',
    'Activity/Description',
    'Activity/StartDate',
    'Activity/EndDate',
    'Activity/Type',
    'Activity/AccountName',
    'Activity/AccountId',
    'Activity/ContactId',
    'Activity/ContactName',
    'Activity/Leader',
    'Activity/LeadName',
    'Activity/LeadId',
    'Activity/OpportunityId',
    'Activity/TicketId',
    'Activity/UserId',
    'Activity/Timeless',
    'Activity/PhoneNumber',
    'Activity/Recurring',
    'Activity/Alarm',
    'Activity/ModifyDate',
    'Activity/Priority',
  ],
  resourceKind: 'userActivities',
  allowSelection: true,
  enableActions: true,
  hashTagQueries: {
    alarm: 'Alarm eq true',
    'status-unconfirmed': 'Status eq "asUnconfirmed"',
    'status-accepted': 'Status eq "asAccepted"',
    'status-declined': 'Status eq "asDeclned"',
    recurring: 'Activity.Recurring eq true',
    timeless: 'Activity.Timeless eq true',
    yesterday: function yesterday() {
      const now = moment();
      const yesterdayStart = now.clone().subtract(1, 'days').startOf('day');
      const yesterdayEnd = yesterdayStart.clone().endOf('day');

      const theQuery = `((Activity.Timeless eq false and Activity.StartDate between @${convert.toIsoStringFromDate(yesterdayStart.toDate())}@ and @${convert.toIsoStringFromDate(yesterdayEnd.toDate())}@) or (Activity.Timeless eq true and Activity.StartDate between @${yesterdayStart.format('YYYY-MM-DDT00:00:00[Z]')}@ and @${yesterdayEnd.format('YYYY-MM-DDT23:59:59[Z]')}@))`;
      return theQuery;
    },
    today: function today() {
      const now = moment();
      const todayStart = now.clone().startOf('day');
      const todayEnd = todayStart.clone().endOf('day');

      const theQuery = `((Activity.Timeless eq false and Activity.StartDate between @${convert.toIsoStringFromDate(todayStart.toDate())}@ and @${convert.toIsoStringFromDate(todayEnd.toDate())}@) or (Activity.Timeless eq true and Activity.StartDate between @${todayStart.format('YYYY-MM-DDT00:00:00[Z]')}@ and @${todayEnd.format('YYYY-MM-DDT23:59:59[Z]')}@))`;
      return theQuery;
    },
    'this-week': function thisWeek() {
      const now = moment();
      const weekStartDate = now.clone().startOf('week');
      const weekEndDate = weekStartDate.clone().endOf('week');

      const theQuery = `((Activity.Timeless eq false and Activity.StartDate between @${convert.toIsoStringFromDate(weekStartDate.toDate())}@ and @${convert.toIsoStringFromDate(weekEndDate.toDate())}@) or (Activity.Timeless eq true and Activity.StartDate between @${weekStartDate.format('YYYY-MM-DDT00:00:00[Z]')}@ and @${weekEndDate.format('YYYY-MM-DDT23:59:59[Z]')}@))`;
      return theQuery;
    },
  },
  hashTagQueriesText: {
    alarm: hashTagResource.hashTagAlarmText,
    'status-unconfirmed': hashTagResource.hashTagUnconfirmedText,
    'status-accepted': hashTagResource.hashTagAcceptedText,
    'status-declined': hashTagResource.hashTagDeclinedText,
    recurring: hashTagResource.hashTagRecurringText,
    timeless: hashTagResource.hashTagTimelessText,
    today: hashTagResource.hashTagTodayText,
    'this-week': hashTagResource.hashTagThisWeekText,
    yesterday: hashTagResource.hashTagYesterdayText,
  },
  createToolLayout: function createToolLayout() {
    this.inherited(arguments);
    if (this.tools && this.tools.tbar && !this._refreshAdded && !window.App.supportsTouch()) {
      this.tools.tbar.push({
        id: 'refresh',
        svg: 'refresh',
        action: '_refreshClicked',
      });

      this._refreshAdded = true;
    }

    return this.tools;
  },
  _refreshAdded: false,
  _refreshClicked: function _refreshClicked() {
    this.clear();
    this.refreshRequired = true;
    this.refresh();

    // Hook for customizers
    this.onRefreshClicked();
  },
  onRefreshClicked: function onRefreshClicked() {},
  _callPhone: function _callPhone(params) {
    this.invokeActionItemBy((theAction) => {
      return theAction.id === 'call';
    }, params.key);
  },
  defaultSearchTerm: function defaultSearchTerm() {
    if (App.enableHashTags) {
      const hashtag = this.hashTagQueriesText['this-week'];
      if (typeof hashtag === 'string' && hashtag.startsWith('#')) {
        return hashtag;
      }

      return `#${hashtag}`;
    }

    return '';
  },
  createActionLayout: function createActionLayout() {
    return this.actions || (this.actions = [{
      id: 'viewAccount',
      label: this.viewAccountActionText,
      enabled: function enabled(theAction, selection) {
        const entry = selection && selection.data;
        if (!entry) {
          return false;
        }
        if (entry.Activity.AccountId) {
          return true;
        }
        return false;
      },
      fn: function fn(theAction, selection) {
        const viewId = 'account_detail';
        const options = {
          key: selection.data.Activity.AccountId,
          descriptor: selection.data.Activity.AccountName,
        };

        const view = App.getView(viewId);
        if (view && options) {
          view.show(options);
        }
      },
    }, {
      id: 'viewOpportunity',
      label: this.viewOpportunityActionText,
      enabled: function enabled(theAction, selection) {
        const entry = selection && selection.data;
        if (!entry) {
          return false;
        }
        if (entry.Activity.OpportunityId) {
          return true;
        }
        return false;
      },
      fn: function fn(theAction, selection) {
        const viewId = 'opportunity_detail';
        const options = {
          key: selection.data.Activity.OpportunityId,
          descriptor: selection.data.Activity.OpportunityName,
        };
        const view = App.getView(viewId);
        if (view && options) {
          view.show(options);
        }
      },
    }, {
      id: 'viewContact',
      label: this.viewContactActionText,
      action: 'navigateToContactOrLead',
      enabled: this.hasContactOrLead,
    }, {
      id: 'complete',
      cls: 'fa fa-check-square fa-2x',
      label: this.completeActivityText,
      enabled: function enabled(theAction, selection) {
        const entry = selection && selection.data;
        if (!entry) {
          return false;
        }

        const recur = entry.Activity.Recurring;

        return entry.Activity.Leader.$key === App.context.user.$key && !recur;
      },
      fn: (function fn(theAction, selection) {
        const entry = selection && selection.data && selection.data.Activity;

        entry.CompletedDate = new Date();
        entry.Result = 'Complete';

        environment.refreshActivityLists();
        this.completeActivity(entry);
      }).bindDelegate(this),
    }, {
      id: 'accept',
      cls: 'fa fa-check fa-2x',
      label: this.acceptActivityText,
      enabled: function enabled(theAction, selection) {
        const entry = selection && selection.data;
        if (!entry) {
          return false;
        }

        return entry.Status === 'asUnconfirmed';
      },
      fn: (function fn(theAction, selection) {
        const entry = selection && selection.data;
        environment.refreshActivityLists();
        this.confirmActivityFor(entry.Activity.$key, App.context.user.$key);
      }).bindDelegate(this),
    }, {
      id: 'decline',
      cls: 'fa fa-ban fa-2x',
      label: this.declineActivityText,
      enabled: function enabled(theAction, selection) {
        const entry = selection && selection.data;
        if (!entry) {
          return false;
        }

        return entry.Status === 'asUnconfirmed';
      },
      fn: (function fn(theAction, selection) {
        const entry = selection && selection.data;

        environment.refreshActivityLists();
        this.declineActivityFor(entry.Activity.$key, App.context.user.$key);
      }).bindDelegate(this),
    }, {
      id: 'call',
      cls: 'phone',
      label: this.callText,
      enabled: function enabled(theAction, selection) {
        const entry = selection && selection.data;
        return entry && entry.Activity && entry.Activity.PhoneNumber;
      },
      fn: function fn(theAction, selection) {
        const entry = selection && selection.data;
        const phone = entry && entry.Activity && entry.Activity.PhoneNumber;
        if (phone) {
          this.recordCallToHistory(function initiateCall() {
            App.initiateCall(phone);
          }.bindDelegate(this), entry);
        }
      }.bindDelegate(this),
    }, {
      id: 'addAttachment',
      cls: 'attach',
      label: this.addAttachmentActionText,
      fn: action.addAttachment.bindDelegate(this),
    }]);
  },
  selectEntry: function selectEntry(params) {
    /* Override selectEntry from the base List mixin.
     * Grabbing a different key here, since we use entry.Activity.$key as the main data-key.
     * TODO: Make [data-key] overrideable in the base class.
     */
    const row = $(`[data-key='${params.key}']`, this.contentNode).first();
    const key = row ? row.attr('data-my-activity-key') : false;

    if (this._selectionModel && key) {
      this._selectionModel.select(key, this.entries[key], row.get(0));
    }

    if (this.options.singleSelect && this.options.singleSelectAction && !this.enableActions) {
      this.invokeSingleSelectAction();
    }
  },
  formatSearchQuery: function formatSearchQuery(searchQuery) {
    return `upper(Activity.Description) like "%${this.escapeSearchQuery(searchQuery.toUpperCase())}%"`;
  },
  declineActivityFor: function declineActivityFor(activityId, userId) {
    this._getUserNotifications(activityId, userId, false);
  },
  confirmActivityFor: function confirmActivityFor(activityId, userId) {
    this._getUserNotifications(activityId, userId, true);
  },
  _getUserNotifications: function _getUserNotifications(activityId, userId, accept) {
    let id = activityId;
    if (activityId) {
      id = activityId.substring(0, 12);
    }

    const req = new Sage.SData.Client.SDataResourceCollectionRequest(this.getService());
    req.setResourceKind('userNotifications');
    req.setContractName('dynamic');
    req.setQueryArg('where', `ActivityId eq '${id}' and ToUser.Id eq '${userId}'`);
    req.setQueryArg('precedence', '0');
    req.read({
      success: function success(userNotifications) {
        if (userNotifications.$resources && userNotifications.$resources.length > 0) {
          if (accept) {
            this.acceptConfirmation(userNotifications.$resources[0]);
          } else {
            this.declineConfirmation(userNotifications.$resources[0]);
          }
        }
      },
      failure: this.onRequestFailure,
      scope: this,
    });
  },
  declineConfirmation: function declineConfirmation(notification) {
    this._postUserNotifications(notification, 'Decline');
  },
  acceptConfirmation: function acceptConfirmation(notification) {
    this._postUserNotifications(notification, 'Accept');
  },
  _postUserNotifications: function _postUserNotifications(notification, operation) {
    if (!notification || typeof operation !== 'string') {
      return;
    }
    /*
     * To get the payload template:
     * http://localhost:6666/SlxClient/slxdata.ashx/slx/dynamic/-/userNotifications/$service/accept/$template?format=json
     */
    const payload = {
      $name: operation,
      request: {
        entity: notification,
        UserNotificationId: notification.$key,
      },
    };

    const request = new Sage.SData.Client.SDataServiceOperationRequest(this.getService())
      .setContractName('dynamic')
      .setResourceKind('usernotifications')
      .setOperationName(operation.toLowerCase());
    request.execute(payload, {
      success: function success() {
        this.clear();
        this.refresh();
      },
      failure: this.onRequestFailure,
      scope: this,
    });
  },
  completeActivity: function completeActivity(entry) {
    const activityModel = App.ModelManager.getModel(MODEL_NAMES.ACTIVITY, MODEL_TYPES.SDATA);
    if (activityModel) {
      activityModel.completeActivity(entry).then(() => {
        connect.publish('/app/refresh', [{
          resourceKind: 'history',
        }]);

        this.clear();
        this.refresh();
      }, (err) => {
        ErrorManager.addError(err, this, {}, 'failure');
      });
    }
  },
  onRequestFailure: function onRequestFailure(response, o) {
    ErrorManager.addError(response, o, {}, 'failure');
  },
  hasAlarm: function hasAlarm(entry) {
    if (entry.Activity && entry.Activity.Alarm === true) {
      return true;
    }

    return false;
  },
  hasBeenTouched: function hasBeenTouched(entry) {
    if (entry.Activity.ModifyDate) {
      const modifiedDate = moment(convert.toDateFromString(entry.Activity.ModifyDate));
      const currentDate = moment().endOf('day');
      const weekAgo = moment().subtract(1, 'weeks');

      return modifiedDate.isAfter(weekAgo) &&
        modifiedDate.isBefore(currentDate);
    }

    return false;
  },
  isImportant: function isImportant(entry) {
    return entry.Activity.Priority === 'High';
  },
  isOverdue: function isOverdue(entry) {
    if (entry.Activity.StartDate) {
      const startDate = convert.toDateFromString(entry.Activity.StartDate);
      const currentDate = new Date();
      const seconds = Math.round((currentDate - startDate) / 1000);
      const mins = seconds / 60;
      if (mins >= 1) {
        return true;
      }
    }
    return false;
  },
  isTimelessToday: function isTimelessToday(entry) {
    if (!entry || !entry.Activity || !entry.Activity.Timeless) {
      return false;
    }

    const start = moment(entry.Activity.StartDate);
    return this._isTimelessToday(start);
  },
  isRecurring: function isRecurring(entry) {
    if (entry.Activity.Recurring) {
      return true;
    }

    return false;
  },
  getItemIconClass: function getItemIconClass(entry) {
    const type = entry && entry.Activity && entry.Activity.Type;
    return this._getItemIconClass(type);
  },
  getItemActionKey: function getItemActionKey(entry) {
    return entry.Activity.$key;
  },
  getItemDescriptor: function getItemDescriptor(entry) {
    return entry.Activity.$descriptor;
  },
  hasContactOrLead: function hasContactOrLead(theAction, selection) {
    return (selection.data.Activity.ContactId) || (selection.data.Activity.LeadId);
  },
  navigateToContactOrLead: function navigateToContactOrLead(theAction, selection) {
    const entry = selection.data.Activity;
    const entity = this.resolveContactOrLeadEntity(entry);
    let viewId;
    let options;

    switch (entity) {
      case 'Contact':
        viewId = 'contact_detail';
        options = {
          key: entry.ContactId,
          descriptor: entry.ContactName,
        };
        break;
      case 'Lead':
        viewId = 'lead_detail';
        options = {
          key: entry.LeadId,
          descriptor: entry.LeadName,
        };
        break;
      default:
    }

    const view = App.getView(viewId);

    if (view && options) {
      view.show(options);
    }
  },
  navigateToDetailView: function navigateToDetailView(key, descriptor, additionalOptions) {
    const myListOptions = additionalOptions || {};
    myListOptions.returnTo = this.id;
    this.inherited(arguments, [key, descriptor, myListOptions]);
  },
  resolveContactOrLeadEntity: function resolveContactOrLeadEntity(entry) {
    const exists = this.existsRE;

    if (entry) {
      if (exists.test(entry.LeadId)) {
        return 'Lead';
      }
      if (exists.test(entry.ContactId)) {
        return 'Contact';
      }
    }
  },
  recordCallToHistory: function recordCallToHistory(complete, entry) {
    const tempEntry = {
      $name: 'History',
      Type: 'atPhoneCall',
      ContactName: entry.Activity.ContactName,
      ContactId: entry.Activity.ContactId,
      AccountName: entry.Activity.AccountName,
      AccountId: entry.Activity.AccountId,
      Description: string.substitute(this.calledText, [entry.Activity.ContactName || '']),
      UserId: App.context && App.context.user.$key,
      UserName: App.context && App.context.user.UserName,
      Duration: 15,
      CompletedDate: (new Date()),
    };

    this.navigateToHistoryInsert('atPhoneCall', tempEntry, complete);
  },

  navigateToHistoryInsert: function navigateToHistoryInsert(type, entry, complete) {
    const view = App.getView(this.historyEditView);
    if (view) {
      environment.refreshActivityLists();
      view.show({
        title: ActivityTypeText[type],
        template: {},
        entry,
        insert: true,
      }, {
        complete,
      });
    }
  },
  createBriefcaseEntity: function createBriefcaseEntity(entry) {
    const entity = {
      entityId: entry.Activity.$key,
      entityName: 'Activity',
      options: {
        includeRelated: true,
        viewId: this.detailView,
        iconClass: this.getItemIconClass(entry),
      },
    };
    return entity;
  },
});

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