1 import {Component, OnInit, ViewChild, TemplateRef} from '@angular/core';
2 import {Observable} from 'rxjs';
3 import {map} from 'rxjs/operators';
4 import {ActivatedRoute} from '@angular/router';
5 import {IdlService, IdlObject} from '@eg/core/idl.service';
6 import {PcrudService} from '@eg/core/pcrud.service';
7 import {AuthService} from '@eg/core/auth.service';
8 import {OrgService} from '@eg/core/org.service';
9 import {ComboboxComponent, ComboboxEntry
10 } from '@eg/share/combobox/combobox.component';
11 import {PrintService} from '@eg/share/print/print.service';
12 import {LocaleService} from '@eg/core/locale.service';
13 import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap';
14 import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
15 import {SampleDataService} from '@eg/share/util/sample-data.service';
16 import {OrgFamily} from '@eg/share/org-family-select/org-family-select.component';
17 import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
20 * Print Template Admin Page
24 templateUrl: 'print-template.component.html'
27 export class PrintTemplateComponent implements OnInit {
29 entries: ComboboxEntry[];
34 localeEntries: ComboboxEntry[];
35 compiledContent: string;
36 templateCache: {[id: number]: IdlObject} = {};
38 selectedOrgs: number[];
40 @ViewChild('templateSelector') templateSelector: ComboboxComponent;
41 @ViewChild('tabs') tabs: NgbTabset;
42 @ViewChild('editDialog') editDialog: FmRecordEditorComponent;
43 @ViewChild('confirmDelete') confirmDelete: ConfirmDialogComponent;
45 // Define some sample data that can be used for various templates
46 // Data will be filled out via the sample data service.
47 // Keys map to print template names
54 private route: ActivatedRoute,
55 private idl: IdlService,
56 private org: OrgService,
57 private pcrud: PcrudService,
58 private auth: AuthService,
59 private locale: LocaleService,
60 private printer: PrintService,
61 private samples: SampleDataService
64 this.localeEntries = [];
68 this.initialOrg = this.auth.user().ws_ou();
69 this.selectedOrgs = [this.initialOrg];
70 this.localeCode = this.locale.currentLocaleCode();
71 this.locale.supportedLocales().subscribe(
72 l => this.localeEntries.push({id: l.code(), label: l.name()}));
73 this.setTemplateInfo().subscribe();
74 this.fleshSampleData();
79 // NOTE: server templates work fine with IDL objects, but
80 // vanilla hashes are easier to work with in the admin UI.
82 // Classes for which sample data exists
83 const classes = ['au', 'ac', 'aua', 'ahr', 'acp', 'mwde'];
84 const samples: any = {};
85 classes.forEach(class_ => samples[class_] =
86 this.idl.toHash(this.samples.listOfThings(class_, 10)));
88 // Wide holds are hashes instead of IDL objects.
89 // Add fields as needed.
91 request_time: this.samples.randomDate().toISOString(),
92 ucard_barcode: samples.ac[0].barcode,
93 usr_family_name: samples.au[0].family_name,
94 usr_alias: samples.au[0].alias,
95 cp_barcode: samples.acp[0].barcode
97 request_time: this.samples.randomDate().toISOString(),
98 ucard_barcode: samples.ac[1].barcode,
99 usr_family_name: samples.au[1].family_name,
100 usr_alias: samples.au[1].alias,
101 cp_barcode: samples.acp[1].barcode
104 this.sampleData.patron_address = {
105 patron: samples.au[0],
106 address: samples.aua[0]
109 this.sampleData.holds_for_bib = wide_holds;
112 onTabChange(evt: NgbTabChangeEvent) {
113 if (evt.nextId === 'template') {
114 this.refreshPreview();
119 // Only present when its tab is visible
120 return document.getElementById('template-preview-pane');
123 // TODO should the ngModelChange handler fire for org-family-select
124 // even when the values don't change?
125 orgOnChange(family: OrgFamily) {
126 // Avoid reundant server calls.
127 if (!this.sameIds(this.selectedOrgs, family.orgIds)) {
128 this.selectedOrgs = family.orgIds;
129 this.setTemplateInfo().subscribe();
133 // True if the 2 arrays contain the same contents,
134 // regardless of the order.
135 sameIds(arr1: any[], arr2: any[]): boolean {
136 if (arr1.length !== arr2.length) {
139 for (let i = 0; i < arr1.length; i++) {
140 if (!arr2.includes(arr1[i])) {
147 localeOnChange(code: string) {
149 this.localeCode = code;
150 this.setTemplateInfo().subscribe();
154 // Fetch name/id for all templates in range.
155 // Avoid fetching the template content until needed.
156 setTemplateInfo(): Observable<IdlObject> {
158 this.template = null;
159 this.templateSelector.applyEntryId(null);
160 this.compiledContent = '';
162 return this.pcrud.search('cpt',
164 owner: this.selectedOrgs,
165 locale: this.localeCode
167 select: {cpt: ['id', 'label', 'owner']},
168 order_by: {cpt: 'label'}
171 this.templateCache[tmpl.id()] = tmpl;
172 this.entries.push({id: tmpl.id(), label: tmpl.label()});
177 getOwnerName(id: number): string {
178 return this.org.get(this.templateCache[id].owner()).shortname();
181 selectTemplate(id: number) {
184 this.template = null;
185 this.compiledContent = '';
189 this.pcrud.retrieve('cpt', id).subscribe(t => {
191 const data = this.sampleData[t.name()];
193 this.sampleJson = JSON.stringify(data, null, 2);
194 this.refreshPreview();
200 if (!this.sampleJson) { return; }
201 this.compiledContent = '';
205 data = JSON.parse(this.sampleJson);
206 this.invalidJson = false;
208 this.invalidJson = true;
211 this.printer.compileRemoteTemplate({
212 templateId: this.template.id(),
214 printContext: 'default' // required, has no impact here
216 }).then(response => {
218 this.compiledContent = response.content;
219 if (response.contentType === 'text/html') {
220 this.container().innerHTML = response.content;
222 // Assumes text/plain or similar
223 this.container().innerHTML = '<pre>' + response.content + '</pre>';
229 this.container().innerHTML = '';
230 this.pcrud.update(this.template).toPromise()
231 .then(() => this.refreshPreview());
235 this.editDialog.setRecord(this.template);
236 this.editDialog.mode = 'update';
237 this.editDialog.open({size: 'lg'}).toPromise().then(id => {
238 if (id !== undefined) {
239 const selectedId = this.template.id();
240 this.setTemplateInfo().toPromise().then(
241 _ => this.selectTemplate(selectedId)
248 const tmpl = this.idl.clone(this.template);
250 this.editDialog.setRecord(tmpl);
251 this.editDialog.mode = 'create';
252 this.editDialog.open({size: 'lg'}).toPromise().then(newTmpl => {
253 if (newTmpl !== undefined) {
254 this.setTemplateInfo().toPromise()
255 .then(_ => this.selectTemplate(newTmpl.id()));
261 this.confirmDelete.open().subscribe(confirmed => {
262 if (!confirmed) { return; }
263 this.pcrud.remove(this.template).toPromise().then(_ => {
264 this.setTemplateInfo().toPromise()
265 .then(x => this.selectTemplate(null));