Remove redundant help link
[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 {name: 'Email'},
19 {name: 'Follow up'},
20 {name: 'Meeting'},
21 {name: 'Phone Call'}
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 }
37 };
38
39 crmCaseType.config(['$routeProvider',
40 function($routeProvider) {
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 });
50 $routeProvider.when('/caseType/:id', {
51 templateUrl: partialUrl('edit.html'),
52 controller: 'CaseTypeCtrl',
53 resolve: {
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 }];
76 }
77 return crmApi(reqs);
78 }
79 }
80 });
81 }
82 ]);
83
84 // Add a new record by name.
85 // Ex: <crmAddName crm-options="['Alpha','Beta','Gamma']" crm-var="newItem" crm-on-add="callMyCreateFunction(newItem)" />
86 crmCaseType.directive('crmAddName', function() {
87 return {
88 restrict: 'AE',
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 });
120
121 scope.$watch(attrs.crmOptions, function(value) {
122 $(input).select2('data', getFormattedOptions);
123 $(input).select2('val', '');
124 });
125 }
126 };
127 });
128
129 crmCaseType.controller('CaseTypeCtrl', function($scope, crmApi, apiCalls) {
130 $scope.partialUrl = partialUrl;
131
132 $scope.activityStatuses = _.values(apiCalls.actStatuses.values);
133 $scope.activityTypes = apiCalls.actTypes.values;
134 $scope.activityTypeNames = _.pluck(apiCalls.actTypes.values, 'name');
135 $scope.relationshipTypeNames = _.pluck(apiCalls.relTypes.values, CRM.crmCaseType.REL_TYPE_CNAME); // CRM_Case_XMLProcessor::REL_TYPE_CNAME
136 $scope.locks = {caseTypeName: true, activitySetName: true};
137
138 $scope.workflows = {
139 'timeline': 'Timeline',
140 'sequence': 'Sequence'
141 };
142
143 $scope.caseType = apiCalls.caseType ? apiCalls.caseType : _.cloneDeep(newCaseTypeTemplate);
144 $scope.caseType.definition = $scope.caseType.definition || [];
145 $scope.caseType.definition.activityTypes = $scope.caseType.definition.activityTypes || [];
146 $scope.caseType.definition.activitySets = $scope.caseType.definition.activitySets || [];
147 $scope.caseType.definition.caseRoles = $scope.caseType.definition.caseRoles || [];
148 window.ct = $scope.caseType;
149
150 $scope.addActivitySet = function(workflow) {
151 var activitySet = {};
152 activitySet[workflow] = '1';
153 activitySet.activityTypes = [];
154
155 var offset = 1;
156 var names = _.pluck($scope.caseType.definition.activitySets, 'name');
157 while (_.contains(names, workflow + '_' + offset)) offset++;
158 activitySet.name = workflow + '_' + offset;
159 activitySet.label = (offset == 1 ) ? $scope.workflows[workflow] : ($scope.workflows[workflow] + ' #' + offset);
160
161 $scope.caseType.definition.activitySets.push(activitySet);
162 _.defer(function() {
163 $('.crmCaseType-acttab').tabs('refresh').tabs({active: -1});
164 });
165 };
166
167 /// Add a new activity entry to an activity-set
168 $scope.addActivity = function(activitySet, activityType) {
169 activitySet.activityTypes.push({
170 name: activityType,
171 status: 'Scheduled',
172 reference_activity: 'Open Case',
173 reference_offset: '1',
174 reference_select: 'newest'
175 });
176 if (!_.contains($scope.activityTypeNames, activityType)) {
177 $scope.activityTypeNames.push(activityType);
178 }
179 };
180
181 /// Add a new top-level activity-type entry
182 $scope.addActivityType = function(activityType) {
183 var names = _.pluck($scope.caseType.definition.activityTypes, 'name');
184 if (!_.contains(names, activityType)) {
185 $scope.caseType.definition.activityTypes.push({
186 name: activityType
187 });
188
189 }
190 if (!_.contains($scope.activityTypeNames, activityType)) {
191 $scope.activityTypeNames.push(activityType);
192 }
193 };
194
195 /// Add a new role
196 $scope.addRole = function(roles, roleName) {
197 var names = _.pluck($scope.caseType.definition.caseRoles, 'name');
198 if (!_.contains(names, roleName)) {
199 roles.push({
200 name: roleName
201 });
202 }
203 if (!_.contains($scope.relationshipTypeNames, roleName)) {
204 $scope.relationshipTypeNames.push(roleName);
205 }
206 };
207
208 $scope.onManagerChange = function(managerRole) {
209 angular.forEach($scope.caseType.definition.caseRoles, function(caseRole) {
210 if (caseRole != managerRole) {
211 caseRole.manager = '0';
212 }
213 });
214 };
215
216 $scope.removeItem = function(array, item) {
217 var idx = _.indexOf(array, item);
218 if (idx != -1) {
219 array.splice(idx, 1);
220 }
221 };
222
223 $scope.isForkable = function() {
224 return !$scope.caseType.id || $scope.caseType.is_forkable
225 };
226
227 $scope.isNewActivitySetAllowed = function(workflow) {
228 switch (workflow) {
229 case 'timeline':
230 return true;
231 case 'sequence':
232 return 0 == _.where($scope.caseType.definition.activitySets, {sequence: '1'}).length;
233 default:
234 CRM.console('warn', 'Denied access to unrecognized workflow: (' + workflow + ')');
235 return false;
236 }
237 };
238
239 $scope.isActivityRemovable = function(activitySet, activity) {
240 if (activitySet.name == 'standard_timeline' && activity.name == 'Open Case') {
241 return false;
242 } else {
243 return true;
244 }
245 };
246
247 $scope.isValidName = function(name) {
248 return !name || name.match(/^[a-zA-Z0-9_]+$/);
249 };
250
251 $scope.getWorkflowName = function(activitySet) {
252 var result = 'Unknown';
253 _.each($scope.workflows, function(value, key) {
254 if (activitySet[key]) result = value;
255 });
256 return result;
257 };
258
259 /**
260 * Determine which HTML partial to use for a particular
261 *
262 * @return string URL of the HTML partial
263 */
264 $scope.activityTableTemplate = function(activitySet) {
265 if (activitySet.timeline) {
266 return partialUrl('timelineTable.html');
267 } else if (activitySet.sequence) {
268 return partialUrl('sequenceTable.html');
269 } else {
270 return '';
271 }
272 };
273
274 $scope.dump = function() {
275 console.log($scope.caseType);
276 };
277
278 $scope.save = function() {
279 var result = crmApi('CaseType', 'create', $scope.caseType, true);
280 result.success(function(data) {
281 if (data.is_error == 0) {
282 $scope.caseType.id = data.id;
283 window.location.href = '#/caseType';
284 }
285 });
286 };
287
288 $scope.$watchCollection('caseType.definition.activitySets', function() {
289 _.defer(function() {
290 $('.crmCaseType-acttab').tabs('refresh');
291 });
292 });
293
294 var updateCaseTypeName = function () {
295 if (!$scope.caseType.id && $scope.locks.caseTypeName) {
296 // Should we do some filtering? Lowercase? Strip whitespace?
297 var t = $scope.caseType.title ? $scope.caseType.title : '';
298 $scope.caseType.name = t.replace(/ /g, '_').replace(/[^a-zA-Z0-9_]/g, '').toLowerCase();
299 }
300 };
301 $scope.$watch('locks.caseTypeName', updateCaseTypeName);
302 $scope.$watch('caseType.title', updateCaseTypeName);
303
304 if (!$scope.isForkable()) {
305 CRM.alert(ts('The CiviCase XML file for this case-type prohibits editing the definition.'));
306 }
307 });
308
309 crmCaseType.controller('CaseTypeListCtrl', function($scope, crmApi, caseTypes) {
310 $scope.caseTypes = caseTypes.values;
311 $scope.toggleCaseType = function (caseType) {
312 caseType.is_active = (caseType.is_active == '1') ? '0' : '1';
313 crmApi('CaseType', 'create', caseType, true)
314 .then(function (data) {
315 if (data.is_error) {
316 caseType.is_active = (caseType.is_active == '1') ? '0' : '1'; // revert
317 $scope.$digest();
318 }
319 });
320 };
321 $scope.deleteCaseType = function (caseType) {
322 crmApi('CaseType', 'delete', {id: caseType.id}, {
323 error: function (data) {
324 CRM.alert(data.error_message, ts('Error'));
325 }
326 })
327 .then(function (data) {
328 if (!data.is_error) {
329 delete caseTypes.values[caseType.id];
330 $scope.$digest();
331 }
332 });
333 };
334 $scope.revertCaseType = function (caseType) {
335 caseType.definition = 'null';
336 caseType.is_forked = '0';
337 crmApi('CaseType', 'create', caseType, true)
338 .then(function (data) {
339 if (data.is_error) {
340 caseType.is_forked = '1'; // restore
341 $scope.$digest();
342 }
343 });
344 };
345 });
346
347 })(angular, CRM.$, CRM._);