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