Merge pull request #3614 from eileenmcnaughton/CRM-14951
[civicrm-core.git] / js / angular-crmCaseType.js
1 (function(angular, $, _) {
2
3 var partialUrl = function(relPath) {
4 return CRM.resourceUrls['civicrm'] + '/partials/crmCaseType/' + relPath;
5 };
6
7 var crmCaseType = angular.module('crmCaseType', ['ngRoute', 'ui.utils', 'crmUi', 'unsavedChanges']);
8
9 // Note: This template will be passed to cloneDeep(), so don't put any funny stuff in here!
10 var newCaseTypeTemplate = {
11 title: "",
12 name: "",
13 is_active: "1",
14 weight: "1",
15 definition: {
16 activityTypes: [
17 {name: 'Open Case', max_instances: 1 }
18 ],
19 activitySets: [
20 {
21 name: 'standard_timeline',
22 label: 'Standard Timeline',
23 timeline: '1', // Angular won't bind checkbox correctly with numeric 1
24 activityTypes: [
25 {name: 'Open Case', status: 'Completed' }
26 ]
27 }
28 ],
29 caseRoles: [
30 { name: 'Case Coordinator', creator: '1', manager: '1'}
31 ]
32 }
33 };
34
35 crmCaseType.config(['$routeProvider',
36 function($routeProvider) {
37 $routeProvider.when('/caseType', {
38 templateUrl: partialUrl('list.html'),
39 controller: 'CaseTypeListCtrl',
40 resolve: {
41 caseTypes: function($route, crmApi) {
42 return crmApi('CaseType', 'get', {});
43 }
44 }
45 });
46 $routeProvider.when('/caseType/:id', {
47 templateUrl: partialUrl('edit.html'),
48 controller: 'CaseTypeCtrl',
49 resolve: {
50 apiCalls: function($route, crmApi) {
51 var reqs = {};
52 reqs.actStatuses = ['OptionValue', 'get', {
53 option_group_id: 'activity_status'
54 }];
55 reqs.actTypes = ['OptionValue', 'get', {
56 option_group_id: 'activity_type',
57 options: {
58 sort: 'name',
59 limit: 0
60 }
61 }];
62 reqs.relTypes = ['RelationshipType', 'get', {
63 options: {
64 sort: CRM.crmCaseType.REL_TYPE_CNAME,
65 limit: 0
66 }
67 }];
68 if ($route.current.params.id !== 'new') {
69 reqs.caseType = ['CaseType', 'getsingle', {
70 id: $route.current.params.id
71 }];
72 }
73 return crmApi(reqs);
74 }
75 }
76 });
77 }
78 ]);
79
80 // Add a new record by name.
81 // Ex: <crmAddName crm-options="['Alpha','Beta','Gamma']" crm-var="newItem" crm-on-add="callMyCreateFunction(newItem)" />
82 crmCaseType.directive('crmAddName', function() {
83 return {
84 restrict: 'AE',
85 template: '<input class="add-activity" type="hidden" />',
86 link: function(scope, element, attrs) {
87 /// Format list of options for select2's "data"
88 var getFormattedOptions = function() {
89 return {
90 results: _.map(scope[attrs.crmOptions], function(option){
91 return {id: option, text: option};
92 })
93 };
94 };
95
96 var input = $('input', element);
97
98 scope._resetSelection = function() {
99 $(input).select2('close');
100 $(input).select2('val', '');
101 scope[attrs.crmVar] = '';
102 };
103
104 $(input).select2({
105 data: getFormattedOptions,
106 createSearchChoice: function(term) {
107 return {id: term, text: term};
108 }
109 });
110 $(input).on('select2-selecting', function(e) {
111 scope[attrs.crmVar] = e.val;
112 scope.$evalAsync(attrs.crmOnAdd);
113 scope.$evalAsync('_resetSelection()');
114 e.preventDefault();
115 });
116
117 scope.$watch(attrs.crmOptions, function(value) {
118 $(input).select2('data', getFormattedOptions);
119 $(input).select2('val', '');
120 });
121 }
122 };
123 });
124
125 crmCaseType.controller('CaseTypeCtrl', function($scope, crmApi, apiCalls) {
126 $scope.partialUrl = partialUrl;
127
128 $scope.activityStatuses = _.values(apiCalls.actStatuses.values);
129 $scope.activityTypes = apiCalls.actTypes.values;
130 $scope.activityTypeNames = _.pluck(apiCalls.actTypes.values, 'name');
131 $scope.relationshipTypeNames = _.pluck(apiCalls.relTypes.values, CRM.crmCaseType.REL_TYPE_CNAME); // CRM_Case_XMLProcessor::REL_TYPE_CNAME
132 $scope.locks = {caseTypeName: true};
133
134 $scope.workflows = {
135 'timeline': 'Timeline',
136 'sequence': 'Sequence'
137 };
138
139 $scope.caseType = apiCalls.caseType ? apiCalls.caseType : _.cloneDeep(newCaseTypeTemplate);
140 $scope.caseType.definition = $scope.caseType.definition || [];
141 $scope.caseType.definition.activityTypes = $scope.caseType.definition.activityTypes || [];
142 $scope.caseType.definition.activitySets = $scope.caseType.definition.activitySets || [];
143 $scope.caseType.definition.caseRoles = $scope.caseType.definition.caseRoles || [];
144 window.ct = $scope.caseType;
145
146 $scope.addActivitySet = function(workflow) {
147 var activitySet = {};
148 activitySet[workflow] = '1';
149 activitySet.activityTypes = [];
150
151 var offset = 1;
152 var names = _.pluck($scope.caseType.definition.activitySets, 'name');
153 while (_.contains(names, workflow + '_' + offset)) offset++;
154 activitySet.name = workflow + '_' + offset;
155 activitySet.label = (offset == 1 ) ? $scope.workflows[workflow] : ($scope.workflows[workflow] + ' #' + offset);
156
157 $scope.caseType.definition.activitySets.push(activitySet);
158 _.defer(function() {
159 $('.crmCaseType-acttab').tabs('refresh').tabs({active: -1});
160 });
161 };
162
163 /// Add a new activity entry to an activity-set
164 $scope.addActivity = function(activitySet, activityType) {
165 activitySet.activityTypes.push({
166 name: activityType,
167 status: 'Scheduled',
168 reference_activity: 'Open Case',
169 reference_offset: '1',
170 reference_select: 'newest'
171 });
172 if (!_.contains($scope.activityTypeNames, activityType)) {
173 $scope.activityTypeNames.push(activityType);
174 }
175 };
176
177 /// Add a new top-level activity-type entry
178 $scope.addActivityType = function(activityType) {
179 var names = _.pluck($scope.caseType.definition.activityTypes, 'name');
180 if (!_.contains(names, activityType)) {
181 $scope.caseType.definition.activityTypes.push({
182 name: activityType
183 });
184
185 }
186 if (!_.contains($scope.activityTypeNames, activityType)) {
187 $scope.activityTypeNames.push(activityType);
188 }
189 };
190
191 /// Add a new role
192 $scope.addRole = function(roles, roleName) {
193 var names = _.pluck($scope.caseType.definition.caseRoles, 'name');
194 if (!_.contains(names, roleName)) {
195 roles.push({
196 name: roleName
197 });
198 }
199 if (!_.contains($scope.relationshipTypeNames, roleName)) {
200 $scope.relationshipTypeNames.push(roleName);
201 }
202 };
203
204 $scope.onManagerChange = function(managerRole) {
205 angular.forEach($scope.caseType.definition.caseRoles, function(caseRole) {
206 if (caseRole != managerRole) {
207 caseRole.manager = '0';
208 }
209 });
210 };
211
212 $scope.removeItem = function(array, item) {
213 var idx = _.indexOf(array, item);
214 if (idx != -1) {
215 array.splice(idx, 1);
216 }
217 };
218
219 $scope.isNewActivitySetAllowed = function(workflow) {
220 switch (workflow) {
221 case 'timeline':
222 return true;
223 case 'sequence':
224 return 0 == _.where($scope.caseType.definition.activitySets, {sequence: '1'}).length;
225 default:
226 if (console && console.log) console.log('Denied access to unrecognized workflow: (' + workflow + ')');
227 return false;
228 }
229 };
230
231 $scope.getWorkflowName = function(activitySet) {
232 var result = 'Unknown';
233 _.each($scope.workflows, function(value, key) {
234 if (activitySet[key]) result = value;
235 });
236 return result;
237 };
238
239 /**
240 * Determine which HTML partial to use for a particular
241 *
242 * @return string URL of the HTML partial
243 */
244 $scope.activityTableTemplate = function(activitySet) {
245 if (activitySet.timeline) {
246 return partialUrl('timelineTable.html');
247 } else if (activitySet.sequence) {
248 return partialUrl('sequenceTable.html');
249 } else {
250 return '';
251 }
252 };
253
254 $scope.dump = function() {
255 console.log($scope.caseType);
256 };
257
258 $scope.save = function() {
259 var result = crmApi('CaseType', 'create', $scope.caseType, true);
260 result.success(function(data) {
261 if (data.is_error == 0) {
262 $scope.caseType.id = data.id;
263 window.location.href = '#/caseType';
264 }
265 });
266 };
267
268 $scope.$watchCollection('caseType.definition.activitySets', function() {
269 _.defer(function() {
270 $('.crmCaseType-acttab').tabs('refresh');
271 });
272 });
273
274 var updateCaseTypeName = function () {
275 if (!$scope.caseType.id && $scope.locks.caseTypeName) {
276 // Should we do some filtering? Lowercase? Strip whitespace?
277 var t = $scope.caseType.title ? $scope.caseType.title : '';
278 $scope.caseType.name = t.replace(/ /g, '_').replace(/[^a-zA-Z0-9_]/g, '').toLowerCase();
279 }
280 };
281 $scope.$watch('locks.caseTypeName', updateCaseTypeName);
282 $scope.$watch('caseType.title', updateCaseTypeName);
283 });
284
285 crmCaseType.controller('CaseTypeListCtrl', function($scope, crmApi, caseTypes) {
286 $scope.caseTypes = caseTypes.values;
287 $scope.toggleCaseType = function (caseType) {
288 caseType.is_active = (caseType.is_active == '1') ? '0' : '1';
289 crmApi('CaseType', 'create', caseType, true)
290 .then(function (data) {
291 if (data.is_error) {
292 caseType.is_active = (caseType.is_active == '1') ? '0' : '1'; // revert
293 $scope.$digest();
294 }
295 });
296 };
297 $scope.deleteCaseType = function (caseType) {
298 crmApi('CaseType', 'delete', {id: caseType.id}, true)
299 .then(function (data) {
300 if (!data.is_error) {
301 delete caseTypes.values[caseType.id];
302 $scope.$digest();
303 }
304 });
305 };
306 });
307
308 })(angular, CRM.$, CRM._);