d0b1092291089400b97ad7d23fe182bd38143037
[evergreen-equinox.git] / Open-ILS / src / eg2 / src / app / staff / admin / local / org-unit-settings / org-unit-settings.component.ts
1 import {Component, OnInit, Input, ViewChild} from '@angular/core';
2 import {Observable, Observer} from 'rxjs';
3 import {Pager} from '@eg/share/util/pager';
4 import {IdlObject, IdlService} from '@eg/core/idl.service';
5 import {OrgService} from '@eg/core/org.service';
6 import {PcrudService} from '@eg/core/pcrud.service';
7 import {AuthService} from '@eg/core/auth.service';
8 import {NetService} from '@eg/core/net.service';
9 import {GridDataSource} from '@eg/share/grid/grid';
10 import {GridComponent} from '@eg/share/grid/grid.component';
11 import {ToastService} from '@eg/share/toast/toast.service';
12
13 import {EditOuSettingDialogComponent
14     } from '@eg/staff/admin/local/org-unit-settings/edit-org-unit-setting-dialog.component';
15 import {OuSettingHistoryDialogComponent
16     } from '@eg/staff/admin/local/org-unit-settings/org-unit-setting-history-dialog.component';
17 import {OuSettingJsonDialogComponent
18     } from '@eg/staff/admin/local/org-unit-settings/org-unit-setting-json-dialog.component';
19
20 export class OrgUnitSetting {
21     name: string;
22     label: string;
23     grp: string;
24     description: string;
25     value: any;
26     value_str: any;
27     dataType: string;
28     fmClass: string;
29     _idlOptions: IdlObject[];
30     _org_unit: IdlObject;
31     context: string;
32     view_perm: string;
33     _history: any[];
34 }
35
36 @Component({
37     templateUrl: './org-unit-settings.component.html'
38 })
39
40 export class OrgUnitSettingsComponent implements OnInit {
41
42     contextOrg: IdlObject;
43
44     initDone = false;
45     gridDataSource: GridDataSource;
46     gridTemplateContext: any;
47     prevFilter: string;
48     currentHistory: any[];
49     currentOptions: any[];
50     jsonFieldData: {};
51     @ViewChild('orgUnitSettingsGrid', { static: true }) orgUnitSettingsGrid: GridComponent;
52
53     @ViewChild('editOuSettingDialog', { static: true })
54         private editOuSettingDialog: EditOuSettingDialogComponent;
55     @ViewChild('orgUnitSettingHistoryDialog', { static: true })
56         private orgUnitSettingHistoryDialog: OuSettingHistoryDialogComponent;
57     @ViewChild('ouSettingJsonDialog', { static: true })
58         private ouSettingJsonDialog: OuSettingJsonDialogComponent;
59
60     refreshSettings: boolean;
61     renderFromPrefs: boolean;
62
63     settingTypeArr: any[];
64
65     @Input() filterString: string;
66
67     constructor(
68         private org: OrgService,
69         private pcrud: PcrudService,
70         private auth: AuthService,
71         private toast: ToastService,
72         private net: NetService,
73     ) {
74         this.gridDataSource = new GridDataSource();
75         this.refreshSettings = true;
76         this.renderFromPrefs = true;
77
78         this.contextOrg = this.org.get(this.auth.user().ws_ou());
79     }
80
81     ngOnInit() {
82         this.initDone = true;
83         this.settingTypeArr = [];
84         this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
85             return this.fetchSettingTypes(pager);
86         };
87         this.orgUnitSettingsGrid.onRowActivate.subscribe((setting: OrgUnitSetting) => {
88             this.showEditSettingValueDialog(setting);
89         });
90     }
91
92     fetchSettingTypes(pager: Pager): Observable<any> {
93         return new Observable<any>(observer => {
94             this.pcrud.retrieveAll('coust', {flesh: 3, flesh_fields: {
95                 'coust': ['grp', 'view_perm']
96             }},
97             { authoritative: true }).subscribe(
98                 settingTypes => this.allocateSettingTypes(settingTypes),
99                 err => {},
100                 ()  => {
101                     this.refreshSettings = false;
102                     this.mergeSettingValues().then(
103                         ok => {
104                             this.flattenSettings(observer);
105                         }
106                     );
107                 }
108             );
109         });
110     }
111
112     mergeSettingValues(): Promise<any> {
113         const settingNames = this.settingTypeArr.map(setting => setting.name);
114         return new Promise((resolve, reject) => {
115             this.net.request(
116                 'open-ils.actor',
117                 'open-ils.actor.ou_setting.ancestor_default.batch',
118                  this.contextOrg.id(), settingNames, this.auth.token()
119             ).subscribe(
120                 blob => {
121                     const settingVals = Object.keys(blob).map(key => {
122                         return {'name': key, 'setting': blob[key]};
123                     });
124                     settingVals.forEach(key => {
125                         if (key.setting) {
126                             const settingsObj = this.settingTypeArr.filter(
127                                 setting => setting.name === key.name
128                             )[0];
129                             settingsObj.value = key.setting.value;
130                             settingsObj.value_str = settingsObj.value;
131                             if (settingsObj.dataType === 'link' && (key.setting.value || key.setting.value === 0)) {
132                                 this.fetchLinkedField(settingsObj.fmClass, key.setting.value, settingsObj.value_str).then(res => {
133                                     settingsObj.value_str = res;
134                                 });
135                             }
136                             settingsObj._org_unit = this.org.get(key.setting.org);
137                             settingsObj.context = settingsObj._org_unit.shortname();
138                         }
139                     });
140                     resolve(this.settingTypeArr);
141                 },
142                 err => reject(err)
143             );
144         });
145     }
146
147     fetchLinkedField(fmClass, id, val) {
148         return new Promise((resolve, reject) => {
149             return this.pcrud.retrieve(fmClass, id).subscribe(linkedField => {
150                 val = linkedField.name();
151                 resolve(val);
152             });
153         });
154     }
155
156     fetchHistory(setting): Promise<any> {
157         const name = setting.name;
158         return new Promise((resolve, reject) => {
159             this.net.request(
160                 'open-ils.actor',
161                 'open-ils.actor.org_unit.settings.history.retrieve',
162                 this.auth.token(), name, this.contextOrg.id()
163             ).subscribe(res => {
164                 this.currentHistory = [];
165                 if (!Array.isArray(res)) {
166                     res = [res];
167                 }
168                 res.forEach(log => {
169                     log.org = this.org.get(log.org);
170                     log.new_value_str = log.new_value;
171                     log.original_value_str = log.original_value;
172                     if (setting.dataType === 'link') {
173                         if (log.new_value) {
174                             this.fetchLinkedField(setting.fmClass, Number(log.new_value), log.new_value_str).then(val => {
175                                 log.new_value_str = val;
176                             });
177                         }
178                         if (log.original_value) {
179                             this.fetchLinkedField(setting.fmClass, Number(log.original_value), log.original_value_str).then(val => {
180                                 log.original_value_str = val;
181                             });
182                         }
183                     }
184                     if (log.new_value_str) { log.new_value_str = log.new_value_str.replace(/^"(.*)"$/, '$1'); }
185                     if (log.original_value_str) { log.original_value_str = log.original_value_str.replace(/^"(.*)"$/, '$1'); }
186                 });
187                 this.currentHistory = res;
188                 this.currentHistory.sort((a, b) => {
189                     return a.date_applied < b.date_applied ? 1 : -1;
190                 });
191
192                 resolve(this.currentHistory);
193             }, err => {reject(err); });
194         });
195     }
196
197     allocateSettingTypes(coust: IdlObject) {
198         const entry = new OrgUnitSetting();
199         entry.name = coust.name();
200         entry.label = coust.label();
201         entry.dataType = coust.datatype();
202         if (coust.fm_class()) { entry.fmClass = coust.fm_class(); }
203         if (coust.description()) { entry.description = coust.description(); }
204         // For some reason some setting types don't have a grp, should look into this...
205         if (coust.grp()) { entry.grp = coust.grp().label(); }
206         if (coust.view_perm()) {
207             entry.view_perm = coust.view_perm().code();
208         }
209
210         this.settingTypeArr.push(entry);
211     }
212
213     flattenSettings(observer: Observer<any>) {
214         this.gridDataSource.data = this.settingTypeArr;
215         observer.complete();
216     }
217
218     contextOrgChanged(org: IdlObject) {
219         this.updateGrid(org);
220     }
221
222     applyFilter(clear?: boolean) {
223         if (clear) { this.filterString = ''; }
224         this.updateGrid(this.contextOrg);
225     }
226
227     updateSetting(obj, entry) {
228         this.net.request(
229             'open-ils.actor',
230             'open-ils.actor.org_unit.settings.update',
231             this.auth.token(), obj.context.id(), obj.setting
232         ).toPromise().then(res => {
233             this.toast.success(entry.label + ' Updated.');
234             if (!obj.setting[entry.name]) {
235                 const settingsObj = this.settingTypeArr.filter(
236                     setting => setting.name === entry.name
237                 )[0];
238                 settingsObj.value = null;
239                 settingsObj.value_str = null;
240                 settingsObj._org_unit = null;
241                 settingsObj.context = null;
242             }
243             this.mergeSettingValues();
244         },
245         err => {
246             this.toast.danger(entry.label + ' failed to update: ' + err.desc);
247         });
248     }
249
250     showEditSettingValueDialog(entry: OrgUnitSetting) {
251         this.editOuSettingDialog.entry = entry;
252         this.editOuSettingDialog.entryValue = entry.value;
253         this.editOuSettingDialog.entryContext = entry._org_unit || this.contextOrg;
254         this.editOuSettingDialog.open({size: 'lg'}).subscribe(
255             res => {
256                 this.updateSetting(res, entry);
257             }
258         );
259     }
260
261     showHistoryDialog(entry: OrgUnitSetting) {
262         if (entry) {
263             this.fetchHistory(entry).then(
264                 fetched => {
265                     this.orgUnitSettingHistoryDialog.history = this.currentHistory;
266                     this.orgUnitSettingHistoryDialog.gridDataSource.data = this.currentHistory;
267                     this.orgUnitSettingHistoryDialog.entry = entry;
268                     this.orgUnitSettingHistoryDialog.open({size: 'lg'}).subscribe(res => {
269                         if (res.revert) {
270                             this.updateSetting(res, entry);
271                         }
272                     });
273                 }
274             );
275         }
276     }
277
278     showJsonDialog(isExport: boolean) {
279         this.ouSettingJsonDialog.isExport = isExport;
280         this.ouSettingJsonDialog.jsonData = '';
281         if (isExport) {
282             this.ouSettingJsonDialog.jsonData = '{';
283             this.gridDataSource.data.forEach(entry => {
284                 this.ouSettingJsonDialog.jsonData +=
285                     '"' + entry.name + '": {"org": "' +
286                     this.contextOrg.id() + '", "value": ';
287                 if (entry.value) {
288                     this.ouSettingJsonDialog.jsonData += '"' + entry.value + '"';
289                 } else {
290                     this.ouSettingJsonDialog.jsonData += 'null';
291                 }
292                 this.ouSettingJsonDialog.jsonData += '}';
293                 if (this.gridDataSource.data.indexOf(entry) !== (this.gridDataSource.data.length - 1)) {
294                     this.ouSettingJsonDialog.jsonData += ',';
295                 }
296             });
297             this.ouSettingJsonDialog.jsonData += '}';
298         }
299
300         this.ouSettingJsonDialog.open({size: 'lg'}).subscribe(res => {
301             if (res.apply && res.jsonData) {
302                 const jsonSettings = JSON.parse(res.jsonData);
303                 Object.entries(jsonSettings).forEach((fields) => {
304                     const entry = this.settingTypeArr.find(x => x.name === fields[0]);
305                     const obj = {setting: {}, context: {}};
306                     const val = this.parseValType(fields[1]['value'], entry.dataType);
307                     obj.setting[fields[0]] = val;
308                     obj.context = this.org.get(fields[1]['org']);
309                     this.updateSetting(obj, entry);
310                 });
311             }
312         });
313     }
314
315     parseValType(value, dataType) {
316         if (dataType === 'integer' || 'currency' || 'link') {
317             return Number(value);
318         } else if (dataType === 'bool') {
319             return (value === 'true');
320         } else {
321             return value;
322         }
323     }
324
325     filterCoust() {
326         if (this.filterString !== this.prevFilter) {
327             this.prevFilter = this.filterString;
328             if (this.filterString) {
329                 this.gridDataSource.data = [];
330                 const tempGrid = this.settingTypeArr;
331                 tempGrid.forEach(row => {
332                     const containsString =
333                          row.name.includes(this.filterString) ||
334                          row.label.includes(this.filterString) ||
335                          (row.grp && row.grp.includes(this.filterString)) ||
336                          (row.description && row.description.includes(this.filterString));
337                     if (containsString) {
338                         this.gridDataSource.data.push(row);
339                     }
340                 });
341             } else {
342                 this.gridDataSource.data = this.settingTypeArr;
343             }
344         }
345     }
346
347     updateGrid(org) {
348         if (this.contextOrg !== org) {
349             this.contextOrg = org;
350             this.refreshSettings = true;
351         }
352
353         if (this.filterString !== this.prevFilter) {
354             this.refreshSettings = true;
355         }
356
357         if (this.refreshSettings) {
358             this.mergeSettingValues().then(
359                 res => this.filterCoust()
360             );
361         }
362     }
363 }