CRM-14481 - crmCaseType - First pass at Angular-based case-type screen (view-only)
authorTim Otten <totten@civicrm.org>
Sat, 19 Apr 2014 22:28:20 +0000 (15:28 -0700)
committerTim Otten <totten@civicrm.org>
Tue, 20 May 2014 03:15:25 +0000 (20:15 -0700)
CRM/Case/Info.php
js/angular-crmCaseType.js [new file with mode: 0644]
partials/crmCaseType/activitySetDetails.html [new file with mode: 0644]
partials/crmCaseType/activityTypesTable.html [new file with mode: 0644]
partials/crmCaseType/caseTypeDetails.html [new file with mode: 0644]
partials/crmCaseType/edit.html [new file with mode: 0644]
partials/crmCaseType/pipelineTable.html [new file with mode: 0644]
partials/crmCaseType/rolesTable.html [new file with mode: 0644]
partials/crmCaseType/timelineTable.html [new file with mode: 0644]

index 8ed9054fd7198fe086c6cdb9178f6b1fc7b02599..4bef8ddd849e4a80a2b1e1944f5613da16330933 100644 (file)
@@ -52,6 +52,15 @@ class CRM_Case_Info extends CRM_Core_Component_Info {
     );
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  public function getAngularModules() {
+    $result = array();
+    $result['crmCaseType'] = array('ext' => 'civicrm', 'files' => array('js/angular-crmCaseType.js'));
+    return $result;
+  }
+
   // docs inherited from interface
   public function getManagedEntities() {
     // Use hook_civicrm_caseTypes to build a list of OptionValues
diff --git a/js/angular-crmCaseType.js b/js/angular-crmCaseType.js
new file mode 100644 (file)
index 0000000..2214efa
--- /dev/null
@@ -0,0 +1,99 @@
+(function(angular, $, _) {
+
+  var partialsUrl = CRM.resourceUrls['civicrm'] + '/partials/crmCaseType';
+  var crmCaseType = angular.module('crmCaseType', ['ngRoute']);
+
+  crmCaseType.config(['$routeProvider',
+    function($routeProvider) {
+      $routeProvider.when('/caseType/:id', {
+        templateUrl: partialsUrl + '/edit.html',
+        controller: 'CaseTypeCtrl'
+      });
+    }
+  ]);
+
+  crmCaseType.controller('CaseTypeCtrl', function($scope) {
+    $scope.partialsUrl = partialsUrl;
+    $scope.caseType = {
+      id: 123,
+      label: 'Adult Day Care Referral',
+      description: 'Superkalafragalisticexpialitotious',
+      is_active: '1', // Angular won't bind checkbox correctly with numeric 1
+      definition: {  // This is the serialized field
+        name: 'Adult Day Care Referral',
+        activityTypes: [
+          {name: 'Open Case', max_instances: 1 },
+          {name: 'Medical evaluation'}
+        ],
+        activitySets: [
+          {
+            name: 'standard_timeline',
+            label: 'Standard Timeline',
+            timeline: '1', // Angular won't bind checkbox correctly with numeric 1
+            activityTypes: [
+              {name: 'Open Case', status: 'Completed' },
+              {name: 'Medical evaluation', reference_activity: 'Open Case', reference_offset: 3, reference_select: 'newest'}
+            ]
+          },
+          {
+            name: 'my_sequence',
+            label: 'Sequence',
+            pipeline: '1', // Angular won't bind checkbox correctly with numeric 1
+            activityTypes: [
+              {name: 'Medical evaluation'},
+              {name: 'Meeting'},
+              {name: 'Phone Call'}
+            ]
+          }
+
+        ],
+        caseRoles: [
+          { name: 'Senior Services Coordinator', creator: '1', manager: '1' },
+          { name: 'Health Services Coordinator' },
+          { name: 'Benefits Specialist' }
+        ]
+      }
+    };
+
+    $scope.onManagerChange = function(managerRole) {
+      angular.forEach($scope.caseType.definition.caseRoles, function(caseRole) {
+        if (caseRole != managerRole) {
+          caseRole.manager = '0';
+        }
+      });
+    };
+
+    $scope.removeItem = function(array, item) {
+      var idx = _.indexOf(array, item);
+      if (idx != -1) {
+        array.splice(idx, 1);
+      }
+    };
+
+    $scope.getWorkflowName = function(activitySet) {
+      if (activitySet.timeline) return "Timeline";
+      if (activitySet.pipeline) return "Sequence";
+      return "Unknown";
+    };
+
+    /**
+     * Determine which HTML partial to use for a particular
+     *
+     * @return string URL of the HTML partial
+     */
+    $scope.activityTableTemplate = function(activitySet) {
+      if (activitySet.timeline) {
+        return partialsUrl + '/timelineTable.html';
+      } else if (activitySet.pipeline) {
+        return partialsUrl + '/pipelineTable.html';
+      } else {
+        return '';
+      }
+    };
+
+    $scope.dump = function() {
+      console.log($scope.caseType);
+    }
+  });
+
+})(angular, CRM.$, CRM._);
\ No newline at end of file
diff --git a/partials/crmCaseType/activitySetDetails.html b/partials/crmCaseType/activitySetDetails.html
new file mode 100644 (file)
index 0000000..bc8287f
--- /dev/null
@@ -0,0 +1,26 @@
+<!--
+Controller: CaseTypeCtrl
+Required vars: activitySet
+-->
+<table class="form-layout-compressed">
+  <tbody>
+  <tr>
+    <td class="label">Label</td>
+    <td>
+      <input type="text" name="label" ng-model="activitySet.label"/>
+    </td>
+  </tr>
+  <tr>
+    <td class="label">Name</td>
+    <td>
+      <input type="text" name="name" ng-model="activitySet.name"/><!-- FIXME lock -->
+    </td>
+  </tr>
+  <tr>
+    <td class="label">Workflow</td>
+    <td>
+      {{ getWorkflowName(activitySet) }}
+    </td>
+  </tr>
+  </tbody>
+</table>
diff --git a/partials/crmCaseType/activityTypesTable.html b/partials/crmCaseType/activityTypesTable.html
new file mode 100644 (file)
index 0000000..d600c2f
--- /dev/null
@@ -0,0 +1,28 @@
+<!--
+Controller: CaseTypeCtrl
+Required vars: caseType
+-->
+<table>
+  <thead>
+  <tr>
+    <th>Activity Type</th>
+    <th>Max Instances</th>
+    <th></th>
+  </tr>
+  </thead>
+
+  <tbody>
+  <tr ng-repeat="activityType in caseType.definition.activityTypes">
+    <td>
+      {{ activityType.name }}
+    </td>
+    <td>
+      {{ activityType.max_instances }}
+    </td>
+    <td>
+      <a class="ui-icon ui-icon-trash" title="Remove"
+         ng-click="removeItem(caseType.definition.activityTypes, activityType)"></a>
+    </td>
+  </tr>
+  </tbody>
+</table>
diff --git a/partials/crmCaseType/caseTypeDetails.html b/partials/crmCaseType/caseTypeDetails.html
new file mode 100644 (file)
index 0000000..c7f89b5
--- /dev/null
@@ -0,0 +1,34 @@
+<!--
+Controller: CaseTypeCtrl
+Required vars: caseType
+
+The original form used table layout; don't know if we have an alternative, CSS-based layout
+-->
+<table class="form-layout-compressed">
+  <tbody>
+  <tr>
+    <td class="label">Label</td>
+    <td>
+      <input type="text" ng-model="caseType.label"/>
+    </td>
+  </tr>
+  <tr>
+    <td class="label">Name</td>
+    <td>
+      <input type="text" ng-model="caseType.definition.name"/> <!-- FIXME lock -->
+    </td>
+  </tr>
+  <tr>
+    <td class="label">Description</td>
+    <td>
+      <textarea ng-model="caseType.description"></textarea>
+    </td>
+  </tr>
+  <tr>
+    <td class="label">Enabled?</td>
+    <td>
+      <input type="checkbox" ng-model="caseType.is_active" ng-true-value="1" ng-false-value="0"/>
+    </td>
+  </tr>
+  </tbody>
+</table>
diff --git a/partials/crmCaseType/edit.html b/partials/crmCaseType/edit.html
new file mode 100644 (file)
index 0000000..c0a208b
--- /dev/null
@@ -0,0 +1,34 @@
+<!--
+Controller: CaseTypeCtrl
+Required vars: caseType
+-->
+<div class="crm-block crm-form-block">
+  <div ng-include="partialsUrl + '/caseTypeDetails.html'"></div>
+
+  <h2>Roles</h2>
+  <div ng-include="partialsUrl + '/rolesTable.html'"></div>
+
+  <h2>Activities</h2>
+
+  <div class="vertical-tabset">
+    <fieldset class="vertical-tab">
+      <legend>Activity Types</legend>
+
+      <div ng-include="partialsUrl + '/activityTypesTable.html'"></div>
+    </fieldset>
+
+    <fieldset class="vertical-tab" ng-repeat="activitySet in caseType.definition.activitySets">
+      <legend>{{ activitySet.label }}</legend>
+
+      <div ng-include="activityTableTemplate(activitySet)"></div>
+
+      <fieldset class="collapsible">
+        <legend>Advanced</legend>
+        <div ng-include="partialsUrl + '/activitySetDetails.html'"></div>
+      </fieldset>
+
+    </fieldset>
+  </div>
+
+  <button ng-click="dump()">Log</button>
+</div>
diff --git a/partials/crmCaseType/pipelineTable.html b/partials/crmCaseType/pipelineTable.html
new file mode 100644 (file)
index 0000000..bd7834c
--- /dev/null
@@ -0,0 +1,22 @@
+<!--
+Controller: CaseTypeCtrl
+Required vars: activitySet
+-->
+<table>
+  <thead>
+  <tr>
+    <th>Activity Type</th>
+    <th></th>
+  </tr>
+  </thead>
+
+  <tbody>
+  <tr ng-repeat="activity in activitySet.activityTypes">
+    <td>{{ activity.name }}</td>
+    <td>
+      <a class="ui-icon ui-icon-trash" title="Remove"
+         ng-click="removeItem(activitySet.activityTypes, activity)"></a>
+    </td>
+  </tr>
+  </tbody>
+</table>
diff --git a/partials/crmCaseType/rolesTable.html b/partials/crmCaseType/rolesTable.html
new file mode 100644 (file)
index 0000000..2b9e3c1
--- /dev/null
@@ -0,0 +1,25 @@
+<!--
+Controller: CaseTypeCtrl
+Required vars: caseType
+-->
+<table>
+  <thead>
+  <tr>
+    <th>Name</th>
+    <th>Assign to Creator</th>
+    <th>Is Manager</th>
+    <th></th>
+  </tr>
+  </thead>
+  <tbody>
+  <tr ng-repeat="relType in caseType.definition.caseRoles | orderBy:'name'">
+    <td>{{relType.name}}</td>
+    <td><input type="checkbox" ng-model="relType.creator" ng-true-value="1" ng-false-value="0"/></td>
+    <td><input type="radio" ng-model="relType.manager" value="1" ng-change="onManagerChange(relType)"/></td>
+    <td>
+      <a class="ui-icon ui-icon-trash" title="Remove"
+         ng-click="removeItem(caseType.definition.caseRoles, relType)"></a>
+    </td>
+  </tr>
+  </tbody>
+</table>
diff --git a/partials/crmCaseType/timelineTable.html b/partials/crmCaseType/timelineTable.html
new file mode 100644 (file)
index 0000000..877362d
--- /dev/null
@@ -0,0 +1,40 @@
+<!--
+Controller: CaseTypeCtrl
+Required vars: activitySet
+-->
+<table>
+  <thead>
+  <tr>
+    <th>Activity Type</th>
+    <th>Status</th>
+    <th>Reference</th>
+    <th>Offset</th>
+    <th>Select</th>
+    <th></th>
+  </tr>
+  </thead>
+
+  <tbody>
+  <tr ng-repeat="activity in activitySet.activityTypes">
+    <td>
+      {{ activity.name }}
+    </td>
+    <td>
+      {{ activity.status }}
+    </td>
+    <td>
+      {{ activity.reference_activity }}
+    </td>
+    <td>
+      {{ activity.reference_offset }}
+    </td>
+    <td>
+      {{ activity.reference_select }}
+    </td>
+    <td>
+      <a class="ui-icon ui-icon-trash" title="Remove"
+         ng-click="removeItem(activitySet.activityTypes, activity)"></a>
+    </td>
+  </tr>
+  </tbody>
+</table>