44da299df0c137ecb13aa52edb8efac2d299a34d
[Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / share / marc-edit / editor.component.ts
1 import {Component, Input, Output, OnInit, EventEmitter, ViewChild} from '@angular/core';
2 import {IdlService} from '@eg/core/idl.service';
3 import {EventService} from '@eg/core/event.service';
4 import {NetService} from '@eg/core/net.service';
5 import {AuthService} from '@eg/core/auth.service';
6 import {OrgService} from '@eg/core/org.service';
7 import {PcrudService} from '@eg/core/pcrud.service';
8 import {ToastService} from '@eg/share/toast/toast.service';
9 import {StringComponent} from '@eg/share/string/string.component';
10 import {MarcRecord} from './marcrecord';
11 import {ComboboxEntry, ComboboxComponent
12   } from '@eg/share/combobox/combobox.component';
13 import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
14
15 interface MarcSavedEvent {
16     marcXml: string;
17     bibSource?: number;
18 }
19
20 /**
21  * MARC Record editor main interface.
22  */
23
24 @Component({
25   selector: 'eg-marc-editor',
26   templateUrl: './editor.component.html'
27 })
28
29 export class MarcEditorComponent implements OnInit {
30
31     record: MarcRecord;
32     editorTab: 'rich' | 'flat';
33     sources: ComboboxEntry[];
34
35     @Input() set recordId(id: number) {
36         if (!id) { return; }
37         if (this.record && this.record.id === id) { return; }
38         this.fromId(id);
39     }
40
41     @Input() set recordXml(xml: string) {
42         if (xml) { this.fromXml(xml); }
43     }
44
45     // Tell us which record source to select by default.
46     // Useful for new records and in-place editing from bare XML.
47     @Input() recordSource: number;
48
49     // If true, saving records to the database is assumed to
50     // happen externally.  IOW, the record editor is just an
51     // in-place MARC modification interface.
52     @Input() inPlaceMode: boolean;
53
54     // In inPlaceMode, this is emitted in lieu of saving the record
55     // in th database.  When inPlaceMode is false, this is emitted after
56     // the record is successfully saved.
57     @Output() recordSaved: EventEmitter<MarcSavedEvent>;
58
59     @ViewChild('sourceSelector', { static: true }) sourceSelector: ComboboxComponent;
60     @ViewChild('confirmDelete', { static: true }) confirmDelete: ConfirmDialogComponent;
61     @ViewChild('confirmUndelete', { static: true }) confirmUndelete: ConfirmDialogComponent;
62     @ViewChild('cannotDelete', { static: true }) cannotDelete: ConfirmDialogComponent;
63     @ViewChild('successMsg', { static: true }) successMsg: StringComponent;
64     @ViewChild('failMsg', { static: true }) failMsg: StringComponent;
65
66     constructor(
67         private evt: EventService,
68         private idl: IdlService,
69         private net: NetService,
70         private auth: AuthService,
71         private org: OrgService,
72         private pcrud: PcrudService,
73         private toast: ToastService
74     ) {
75         this.sources = [];
76         this.recordSaved = new EventEmitter<MarcSavedEvent>();
77     }
78
79     ngOnInit() {
80         // Default to flat for now since it's all that's supported.
81         this.editorTab = 'flat';
82
83         this.pcrud.retrieveAll('cbs').subscribe(
84             src => this.sources.push({id: +src.id(), label: src.source()}),
85             _ => {},
86             () => {
87                 this.sources = this.sources.sort((a, b) =>
88                     a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1
89                 );
90
91                 if (this.recordSource) {
92                     this.sourceSelector.applyEntryId(this.recordSource);
93                 }
94             }
95         );
96     }
97
98     saveRecord(): Promise<any> {
99         const xml = this.record.toXml();
100
101         let sourceName: string = null;
102         let sourceId: number = null;
103
104         if (this.sourceSelector.selected) {
105             sourceName = this.sourceSelector.selected.label;
106             sourceId = this.sourceSelector.selected.id;
107         }
108
109         if (this.inPlaceMode) {
110             // Let the caller have the modified XML and move on.
111             this.recordSaved.emit({marcXml: xml, bibSource: sourceId});
112             return Promise.resolve();
113         }
114
115         if (this.record.id) { // Editing an existing record
116
117             const method = 'open-ils.cat.biblio.record.xml.update';
118
119             return this.net.request('open-ils.cat', method,
120                 this.auth.token(), this.record.id, xml, sourceName
121             ).toPromise().then(response => {
122
123                 const evt = this.evt.parse(response);
124                 if (evt) {
125                     console.error(evt);
126                     this.failMsg.current().then(msg => this.toast.warning(msg));
127                     return;
128                 }
129
130                 this.successMsg.current().then(msg => this.toast.success(msg));
131                 this.recordSaved.emit({marcXml: xml, bibSource: sourceId});
132                 return response;
133             });
134
135         } else {
136             // TODO: create a new record
137         }
138     }
139
140     fromId(id: number): Promise<any> {
141         return this.pcrud.retrieve('bre', id)
142         .toPromise().then(bib => {
143             this.record = new MarcRecord(bib.marc());
144             this.record.id = id;
145             this.record.deleted = bib.deleted() === 't';
146             if (bib.source()) {
147                 this.sourceSelector.applyEntryId(+bib.source());
148             }
149         });
150     }
151
152     fromXml(xml: string) {
153         this.record = new MarcRecord(xml);
154         this.record.id = null;
155     }
156
157     deleteRecord(): Promise<any> {
158
159         return this.confirmDelete.open().toPromise()
160         .then(yes => {
161             if (!yes) { return; }
162
163             return this.net.request('open-ils.cat',
164                 'open-ils.cat.biblio.record_entry.delete',
165                 this.auth.token(), this.record.id).toPromise()
166
167             .then(resp => {
168
169                 const evt = this.evt.parse(resp);
170                 if (evt) {
171                     if (evt.textcode === 'RECORD_NOT_EMPTY') {
172                         return this.cannotDelete.open().toPromise();
173                     } else {
174                         console.error(evt);
175                         return alert(evt);
176                     }
177                 }
178                 return this.fromId(this.record.id)
179                 .then(_ => this.recordSaved.emit(
180                     {marcXml: this.record.toXml()}));
181             });
182         });
183     }
184
185     undeleteRecord(): Promise<any> {
186
187         return this.confirmUndelete.open().toPromise()
188         .then(yes => {
189             if (!yes) { return; }
190
191             return this.net.request('open-ils.cat',
192                 'open-ils.cat.biblio.record_entry.undelete',
193                 this.auth.token(), this.record.id).toPromise()
194
195             .then(resp => {
196
197                 const evt = this.evt.parse(resp);
198                 if (evt) { console.error(evt); return alert(evt); }
199
200                 return this.fromId(this.record.id)
201                 .then(_ => this.recordSaved.emit(
202                     {marcXml: this.record.toXml()}));
203             });
204         });
205     }
206 }
207