Merge pull request #11235 from clnlf/CRM-21389-UserDashBoard-Regions
[civicrm-core.git] / ang / crmCaseType.js
CommitLineData
4c58e251
TO
1(function(angular, $, _) {
2
0b199194 3 var crmCaseType = angular.module('crmCaseType', CRM.angRequires('crmCaseType'));
4c58e251 4
506cd414 5 // Note: This template will be passed to cloneDeep(), so don't put any funny stuff in here!
9be5fc34
TO
6 var newCaseTypeTemplate = {
7 title: "",
8 name: "",
9 is_active: "1",
10 weight: "1",
11 definition: {
12 activityTypes: [
4d8bbcf6
TO
13 {name: 'Open Case', max_instances: 1},
14 {name: 'Email'},
15 {name: 'Follow up'},
16 {name: 'Meeting'},
17 {name: 'Phone Call'}
9be5fc34
TO
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 }
4d74de55
TO
33 };
34
4c58e251
TO
35 crmCaseType.config(['$routeProvider',
36 function($routeProvider) {
b75c2546 37 $routeProvider.when('/caseType', {
ef5d18a1 38 templateUrl: '~/crmCaseType/list.html',
b75c2546
TO
39 controller: 'CaseTypeListCtrl',
40 resolve: {
41 caseTypes: function($route, crmApi) {
a214ce43 42 return crmApi('CaseType', 'get', {options: {limit: 0}});
b75c2546
TO
43 }
44 }
45 });
4c58e251 46 $routeProvider.when('/caseType/:id', {
ef5d18a1 47 templateUrl: '~/crmCaseType/edit.html',
4d74de55
TO
48 controller: 'CaseTypeCtrl',
49 resolve: {
9625aad1
TO
50 apiCalls: function($route, crmApi) {
51 var reqs = {};
52 reqs.actStatuses = ['OptionValue', 'get', {
7c2b40d1
CW
53 option_group_id: 'activity_status',
54 sequential: 1,
55 options: {limit: 0}
56 }];
57 reqs.caseStatuses = ['OptionValue', 'get', {
58 option_group_id: 'case_status',
59 sequential: 1,
60 options: {limit: 0}
9625aad1
TO
61 }];
62 reqs.actTypes = ['OptionValue', 'get', {
63 option_group_id: 'activity_type',
4324b8d7 64 sequential: 1,
9625aad1
TO
65 options: {
66 sort: 'name',
67 limit: 0
68 }
69 }];
70 reqs.relTypes = ['RelationshipType', 'get', {
4324b8d7 71 sequential: 1,
9625aad1
TO
72 options: {
73 sort: CRM.crmCaseType.REL_TYPE_CNAME,
74 limit: 0
75 }
76 }];
77 if ($route.current.params.id !== 'new') {
78 reqs.caseType = ['CaseType', 'getsingle', {
79 id: $route.current.params.id
80 }];
87dcd909 81 }
9625aad1 82 return crmApi(reqs);
4d74de55
TO
83 }
84 }
4c58e251
TO
85 });
86 }
87 ]);
88
95fd24c0
TO
89 // Add a new record by name.
90 // Ex: <crmAddName crm-options="['Alpha','Beta','Gamma']" crm-var="newItem" crm-on-add="callMyCreateFunction(newItem)" />
8fc6fba7 91 crmCaseType.directive('crmAddName', function() {
95fd24c0
TO
92 return {
93 restrict: 'AE',
9597c394 94 template: '<input class="add-activity crm-action-menu fa-plus" type="hidden" />',
bafce1db 95 link: function(scope, element, attrs) {
bafce1db
TO
96
97 var input = $('input', element);
98
99 scope._resetSelection = function() {
100 $(input).select2('close');
101 $(input).select2('val', '');
102 scope[attrs.crmVar] = '';
103 };
104
4324b8d7
CW
105 $(input).crmSelect2({
106 data: scope[attrs.crmOptions],
bafce1db 107 createSearchChoice: function(term) {
4324b8d7 108 return {id: term, text: term + ' (' + ts('new') + ')'};
00eee619 109 },
4324b8d7 110 createSearchChoicePosition: 'bottom',
00eee619 111 placeholder: attrs.placeholder
bafce1db
TO
112 });
113 $(input).on('select2-selecting', function(e) {
114 scope[attrs.crmVar] = e.val;
115 scope.$evalAsync(attrs.crmOnAdd);
116 scope.$evalAsync('_resetSelection()');
117 e.preventDefault();
118 });
60dd172b
TO
119
120 scope.$watch(attrs.crmOptions, function(value) {
4324b8d7 121 $(input).select2('data', scope[attrs.crmOptions]);
60dd172b
TO
122 $(input).select2('val', '');
123 });
bafce1db 124 }
95fd24c0
TO
125 };
126 });
127
9625aad1 128 crmCaseType.controller('CaseTypeCtrl', function($scope, crmApi, apiCalls) {
4324b8d7
CW
129 // CRM_Case_XMLProcessor::REL_TYPE_CNAME
130 var REL_TYPE_CNAME = CRM.crmCaseType.REL_TYPE_CNAME,
131
132 ts = $scope.ts = CRM.ts(null);
76e4acb8 133
7c2b40d1
CW
134 $scope.activityStatuses = apiCalls.actStatuses.values;
135 $scope.caseStatuses = _.indexBy(apiCalls.caseStatuses.values, 'name');
4324b8d7
CW
136 $scope.activityTypes = _.indexBy(apiCalls.actTypes.values, 'name');
137 $scope.activityTypeOptions = _.map(apiCalls.actTypes.values, formatActivityTypeOption);
138 $scope.relationshipTypeOptions = _.map(apiCalls.relTypes.values, function(type) {
139 return {id: type[REL_TYPE_CNAME], text: type.label_b_a};
140 });
1317b266 141 $scope.locks = {caseTypeName: true, activitySetName: true};
dc7a657e 142
76e4acb8
TO
143 $scope.workflows = {
144 'timeline': 'Timeline',
b387506c 145 'sequence': 'Sequence'
76e4acb8
TO
146 };
147
9625aad1 148 $scope.caseType = apiCalls.caseType ? apiCalls.caseType : _.cloneDeep(newCaseTypeTemplate);
87dcd909 149 $scope.caseType.definition = $scope.caseType.definition || [];
4d74de55
TO
150 $scope.caseType.definition.activityTypes = $scope.caseType.definition.activityTypes || [];
151 $scope.caseType.definition.activitySets = $scope.caseType.definition.activitySets || [];
be5aae33
AH
152 _.each($scope.caseType.definition.activitySets, function (set) {
153 _.each(set.activityTypes, function (type, name) {
154 type.label = $scope.activityTypes[type.name].label;
155 });
156 });
4d74de55 157 $scope.caseType.definition.caseRoles = $scope.caseType.definition.caseRoles || [];
7c2b40d1
CW
158 $scope.caseType.definition.statuses = $scope.caseType.definition.statuses || [];
159
160 $scope.selectedStatuses = {};
161 _.each(apiCalls.caseStatuses.values, function (status) {
162 $scope.selectedStatuses[status.name] = !$scope.caseType.definition.statuses.length || $scope.caseType.definition.statuses.indexOf(status.name) > -1;
163 });
4c58e251 164
76e4acb8
TO
165 $scope.addActivitySet = function(workflow) {
166 var activitySet = {};
167 activitySet[workflow] = '1';
168 activitySet.activityTypes = [];
169
170 var offset = 1;
171 var names = _.pluck($scope.caseType.definition.activitySets, 'name');
172 while (_.contains(names, workflow + '_' + offset)) offset++;
173 activitySet.name = workflow + '_' + offset;
174 activitySet.label = (offset == 1 ) ? $scope.workflows[workflow] : ($scope.workflows[workflow] + ' #' + offset);
175
176 $scope.caseType.definition.activitySets.push(activitySet);
177 _.defer(function() {
178 $('.crmCaseType-acttab').tabs('refresh').tabs({active: -1});
179 });
180 };
181
4324b8d7
CW
182 function formatActivityTypeOption(type) {
183 return {id: type.name, text: type.label, icon: type.icon};
184 }
185
186 function addActivityToSet(activitySet, activityTypeName) {
d7c25f6c 187 activitySet.activityTypes.push({
4324b8d7 188 name: activityTypeName,
be5aae33 189 label: $scope.activityTypes[activityTypeName].label,
183241d8 190 status: 'Scheduled',
191 reference_activity: 'Open Case',
192 reference_offset: '1',
193 reference_select: 'newest'
d7c25f6c 194 });
4324b8d7
CW
195 }
196
197 function createActivity(name, callback) {
198 CRM.loadForm(CRM.url('civicrm/admin/options/activity_type', {action: 'add', reset: 1, label: name, component_id: 7}))
199 .on('crmFormSuccess', function(e, data) {
200 $scope.activityTypes[data.optionValue.name] = data.optionValue;
201 $scope.activityTypeOptions.push(formatActivityTypeOption(data.optionValue));
202 callback(data.optionValue);
203 $scope.$digest();
204 });
205 }
206
207 // Add a new activity entry to an activity-set
208 $scope.addActivity = function(activitySet, activityType) {
209 if ($scope.activityTypes[activityType]) {
210 addActivityToSet(activitySet, activityType);
211 } else {
212 createActivity(activityType, function(newActivity) {
213 addActivityToSet(activitySet, newActivity.name);
214 });
60dd172b 215 }
d7c25f6c
TO
216 };
217
218 /// Add a new top-level activity-type entry
219 $scope.addActivityType = function(activityType) {
220 var names = _.pluck($scope.caseType.definition.activityTypes, 'name');
221 if (!_.contains(names, activityType)) {
4324b8d7
CW
222 // Add an activity type that exists
223 if ($scope.activityTypes[activityType]) {
224 $scope.caseType.definition.activityTypes.push({name: activityType});
225 } else {
226 createActivity(activityType, function(newActivity) {
227 $scope.caseType.definition.activityTypes.push({name: newActivity.name});
228 });
229 }
d7c25f6c
TO
230 }
231 };
232
8c7e0ae8
TO
233 /// Add a new role
234 $scope.addRole = function(roles, roleName) {
bafce1db
TO
235 var names = _.pluck($scope.caseType.definition.caseRoles, 'name');
236 if (!_.contains(names, roleName)) {
4324b8d7
CW
237 if (_.where($scope.relationshipTypeOptions, {id: roleName}).length) {
238 roles.push({name: roleName});
239 } else {
240 CRM.loadForm(CRM.url('civicrm/admin/reltype', {action: 'add', reset: 1, label_a_b: roleName, label_b_a: roleName}))
241 .on('crmFormSuccess', function(e, data) {
242 roles.push({name: data.relationshipType[REL_TYPE_CNAME]});
243 $scope.relationshipTypeOptions.push({id: data.relationshipType[REL_TYPE_CNAME], text: data.relationshipType.label_b_a});
244 $scope.$digest();
245 });
246 }
60dd172b 247 }
8c7e0ae8
TO
248 };
249
4c58e251
TO
250 $scope.onManagerChange = function(managerRole) {
251 angular.forEach($scope.caseType.definition.caseRoles, function(caseRole) {
252 if (caseRole != managerRole) {
253 caseRole.manager = '0';
254 }
255 });
256 };
257
258 $scope.removeItem = function(array, item) {
259 var idx = _.indexOf(array, item);
260 if (idx != -1) {
261 array.splice(idx, 1);
262 }
263 };
264
b40b4114 265 $scope.isForkable = function() {
f2bad133 266 return !$scope.caseType.id || $scope.caseType.is_forkable;
b40b4114
TO
267 };
268
7c2b40d1
CW
269 $scope.newStatus = function() {
270 CRM.loadForm(CRM.url('civicrm/admin/options/case_status', {action: 'add', reset: 1}))
271 .on('crmFormSuccess', function(e, data) {
272 $scope.caseStatuses[data.optionValue.name] = data.optionValue;
273 $scope.selectedStatuses[data.optionValue.name] = true;
274 $scope.$digest();
275 });
276 };
277
5d973e24
TO
278 $scope.isNewActivitySetAllowed = function(workflow) {
279 switch (workflow) {
280 case 'timeline':
281 return true;
b387506c 282 case 'sequence':
b04e5ffb 283 return 0 === _.where($scope.caseType.definition.activitySets, {sequence: '1'}).length;
5d973e24 284 default:
bba9b4f0 285 CRM.console('warn', 'Denied access to unrecognized workflow: (' + workflow + ')');
5d973e24
TO
286 return false;
287 }
288 };
289
259a7652 290 $scope.isActivityRemovable = function(activitySet, activity) {
12b84ade 291 return true;
259a7652
TO
292 };
293
f42b448f
TO
294 $scope.isValidName = function(name) {
295 return !name || name.match(/^[a-zA-Z0-9_]+$/);
296 };
297
4c58e251 298 $scope.getWorkflowName = function(activitySet) {
76e4acb8
TO
299 var result = 'Unknown';
300 _.each($scope.workflows, function(value, key) {
301 if (activitySet[key]) result = value;
302 });
303 return result;
4c58e251
TO
304 };
305
306 /**
307 * Determine which HTML partial to use for a particular
308 *
309 * @return string URL of the HTML partial
310 */
311 $scope.activityTableTemplate = function(activitySet) {
312 if (activitySet.timeline) {
ef5d18a1 313 return '~/crmCaseType/timelineTable.html';
b387506c 314 } else if (activitySet.sequence) {
ef5d18a1 315 return '~/crmCaseType/sequenceTable.html';
4c58e251
TO
316 } else {
317 return '';
318 }
319 };
320
321 $scope.dump = function() {
322 console.log($scope.caseType);
76e4acb8
TO
323 };
324
aa1a7c2e 325 $scope.save = function() {
7c2b40d1
CW
326 // Add selected statuses
327 var selectedStatuses = [];
328 _.each($scope.selectedStatuses, function(v, k) {
329 if (v) selectedStatuses.push(k);
330 });
331 // Ignore if ALL or NONE selected
332 $scope.caseType.definition.statuses = selectedStatuses.length == _.size($scope.selectedStatuses) ? [] : selectedStatuses;
c7bccb5f 333 var result = crmApi('CaseType', 'create', $scope.caseType, true);
3140a415 334 result.then(function(data) {
b04e5ffb 335 if (data.is_error === 0 || data.is_error == '0') {
c7bccb5f 336 $scope.caseType.id = data.id;
1ab5b88e 337 window.location.href = '#/caseType';
c7bccb5f 338 }
339 });
aa1a7c2e
TO
340 };
341
76e4acb8
TO
342 $scope.$watchCollection('caseType.definition.activitySets', function() {
343 _.defer(function() {
8fc6fba7 344 $('.crmCaseType-acttab').tabs('refresh');
76e4acb8
TO
345 });
346 });
685acae4 347
348 var updateCaseTypeName = function () {
349 if (!$scope.caseType.id && $scope.locks.caseTypeName) {
350 // Should we do some filtering? Lowercase? Strip whitespace?
a5ca1f48 351 var t = $scope.caseType.title ? $scope.caseType.title : '';
352 $scope.caseType.name = t.replace(/ /g, '_').replace(/[^a-zA-Z0-9_]/g, '').toLowerCase();
685acae4 353 }
354 };
355 $scope.$watch('locks.caseTypeName', updateCaseTypeName);
356 $scope.$watch('caseType.title', updateCaseTypeName);
b40b4114
TO
357
358 if (!$scope.isForkable()) {
359 CRM.alert(ts('The CiviCase XML file for this case-type prohibits editing the definition.'));
360 }
4c58e251
TO
361 });
362
b75c2546 363 crmCaseType.controller('CaseTypeListCtrl', function($scope, crmApi, caseTypes) {
7abbf317
CW
364 var ts = $scope.ts = CRM.ts(null);
365
b75c2546 366 $scope.caseTypes = caseTypes.values;
4b8c8b42
TO
367 $scope.toggleCaseType = function (caseType) {
368 caseType.is_active = (caseType.is_active == '1') ? '0' : '1';
369 crmApi('CaseType', 'create', caseType, true)
c99f1a0a
TO
370 .catch(function (data) {
371 caseType.is_active = (caseType.is_active == '1') ? '0' : '1'; // revert
372 $scope.$digest();
4b8c8b42
TO
373 });
374 };
375 $scope.deleteCaseType = function (caseType) {
eb8e4c2d
TO
376 crmApi('CaseType', 'delete', {id: caseType.id}, {
377 error: function (data) {
7abbf317 378 CRM.alert(data.error_message, ts('Error'), 'error');
eb8e4c2d
TO
379 }
380 })
4b8c8b42 381 .then(function (data) {
c99f1a0a
TO
382 delete caseTypes.values[caseType.id];
383 $scope.$digest();
4b8c8b42
TO
384 });
385 };
470a458e
TO
386 $scope.revertCaseType = function (caseType) {
387 caseType.definition = 'null';
388 caseType.is_forked = '0';
389 crmApi('CaseType', 'create', caseType, true)
c99f1a0a
TO
390 .catch(function (data) {
391 caseType.is_forked = '1'; // restore
392 $scope.$digest();
470a458e
TO
393 });
394 };
b75c2546
TO
395 });
396
bba9b4f0 397})(angular, CRM.$, CRM._);