LP1816475: Booking module refresh
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / booking / create-reservation-dialog.component.ts
1 import {Component, Input, Output, OnInit, ViewChild, EventEmitter} from '@angular/core';
2 import {FormGroup, FormControl, Validators, ValidatorFn, ValidationErrors} from '@angular/forms';
3 import {Router} from '@angular/router';
4 import {Observable, of} from 'rxjs';
5 import {switchMap, single, startWith, tap} from 'rxjs/operators';
6 import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
7 import {AuthService} from '@eg/core/auth.service';
8 import {FormatService} from '@eg/core/format.service';
9 import {IdlObject} from '@eg/core/idl.service';
10 import {NetService} from '@eg/core/net.service';
11 import {OrgService} from '@eg/core/org.service';
12 import {PcrudService} from '@eg/core/pcrud.service';
13 import {DialogComponent} from '@eg/share/dialog/dialog.component';
14 import {PatronBarcodeValidator} from '@eg/share/validators/patron_barcode_validator.directive';
15 import {ToastService} from '@eg/share/toast/toast.service';
16 import {AlertDialogComponent} from '@eg/share/dialog/alert.component';
17 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
18 import * as Moment from 'moment-timezone';
19
20 const startTimeIsBeforeEndTimeValidator: ValidatorFn = (fg: FormGroup): ValidationErrors | null => {
21     const start = fg.get('startTime').value;
22     const end = fg.get('endTime').value;
23     return start !== null && end !== null &&
24         start.isBefore(end)
25         ? null
26         : { startTimeNotBeforeEndTime: true };
27 };
28
29 @Component({
30   selector: 'eg-create-reservation-dialog',
31   templateUrl: './create-reservation-dialog.component.html'
32 })
33
34 export class CreateReservationDialogComponent
35     extends DialogComponent implements OnInit {
36
37     @Input() targetResource: number;
38     @Input() targetResourceBarcode: string;
39     @Input() targetResourceType: ComboboxEntry;
40     @Input() attributes: number[] = [];
41     @Input() resources: IdlObject[] = [];
42     @Output() onComplete: EventEmitter<boolean>;
43
44     create: FormGroup;
45     patron$: Observable<{first_given_name: string, second_given_name: string, family_name: string}>;
46     pickupLibId: number;
47     timezone: string = this.format.wsOrgTimezone;
48     pickupLibraryUsesDifferentTz: boolean;
49
50     public disableOrgs: () => number[];
51     addBresv$: () => Observable<any>;
52     @ViewChild('fail') private fail: AlertDialogComponent;
53
54     handlePickupLibChange: ($event: IdlObject) => void;
55
56     constructor(
57         private auth: AuthService,
58         private format: FormatService,
59         private net: NetService,
60         private org: OrgService,
61         private pcrud: PcrudService,
62         private router: Router,
63         private modal: NgbModal,
64         private pbv: PatronBarcodeValidator,
65         private toast: ToastService
66     ) {
67         super(modal);
68         this.onComplete = new EventEmitter<boolean>();
69     }
70
71     ngOnInit() {
72
73         this.create = new FormGroup({
74             // TODO: replace this control with a patron search form
75             // when available in the Angular client
76             'patronBarcode': new FormControl('',
77                 [Validators.required],
78                 [this.pbv.validate]
79             ),
80             'emailNotify': new FormControl(true),
81             'startTime': new FormControl(),
82             'endTime': new FormControl(),
83             'resourceList': new FormControl(),
84         }, [startTimeIsBeforeEndTimeValidator]
85         );
86
87         this.addBresv$ = () => {
88             let selectedResourceId = this.targetResource ? [this.targetResource] : null;
89             if (!selectedResourceId &&
90                 this.resourceListSelection !== null &&
91                 'any' !== this.resourceListSelection.id) {
92                 selectedResourceId = [this.resourceListSelection.id];
93             }
94             return this.net.request(
95                 'open-ils.booking',
96                 'open-ils.booking.reservations.create',
97                 this.auth.token(),
98                 this.patronBarcode.value,
99                 this.selectedTimes,
100                 this.pickupLibId,
101                 this.targetResourceType.id,
102                 selectedResourceId,
103                 this.attributes.filter(Boolean),
104                 this.emailNotify
105             ).pipe(tap(
106                 (success) => {
107                     if (success.ilsevent) {
108                         console.warn(success);
109                         this.fail.open();
110                     } else {
111                         this.toast.success('Reservation successfully created');
112                         console.debug(success);
113                         this.close();
114                    }
115                 }, (fail) => {
116                     console.warn(fail);
117                     this.fail.open();
118                 }, () => this.onComplete.emit(true)
119             ));
120         };
121
122         this.handlePickupLibChange = ($event) => {
123             this.pickupLibId = $event.id();
124             this.org.settings('lib.timezone', this.pickupLibId).then((tz) => {
125                 this.timezone = tz['lib.timezone'] || this.format.wsOrgTimezone;
126                 this.pickupLibraryUsesDifferentTz = (tz['lib.timezone'] && (this.format.wsOrgTimezone !== tz['lib.timezone']));
127             });
128         };
129
130         this.disableOrgs = () => this.org.filterList( { canHaveVolumes : false }, true);
131
132         this.patron$ = this.patronBarcode.statusChanges.pipe(
133             startWith({first_given_name: '', second_given_name: '', family_name: ''}),
134             switchMap(() => {
135                 if ('VALID' === this.patronBarcode.status) {
136                     return this.net.request(
137                         'open-ils.actor',
138                         'open-ils.actor.get_barcodes',
139                         this.auth.token(),
140                         this.auth.user().ws_ou(),
141                         'actor', this.patronBarcode.value).pipe(
142                             single(),
143                             switchMap((result) => {
144                                 return this.pcrud.retrieve('au', result[0]['id']).pipe(
145                                     switchMap((au) => {
146                                         return of({
147                                             first_given_name: au.first_given_name(),
148                                             second_given_name: au.second_given_name(),
149                                             family_name: au.family_name()});
150                                     })
151                                 );
152                             })
153                         );
154                 } else {
155                     return of({
156                         first_given_name: '',
157                         second_given_name: '',
158                         family_name: ''
159                     });
160                 }
161             })
162         );
163     }
164
165     setDefaultTimes(times: Moment[], granularity: number) {
166         this.create.patchValue({startTime: Moment.min(times),
167         endTime: Moment.max(times).clone().add(granularity, 'minutes')
168         });
169     }
170
171     openPatronReservations = (): void => {
172         this.net.request(
173             'open-ils.actor',
174             'open-ils.actor.get_barcodes',
175             this.auth.token(),
176             this.auth.user().ws_ou(),
177             'actor', this.patronBarcode.value
178         ).subscribe((patron) => this.router.navigate(['/staff', 'booking', 'manage_reservations', 'by_patron', patron[0]['id']]));
179     }
180
181     addBresvAndOpenPatronReservations = (): void => {
182         this.addBresv$()
183         .subscribe(() => this.openPatronReservations());
184     }
185
186     get emailNotify() {
187         return this.create.get('emailNotify').value;
188     }
189
190     get patronBarcode() {
191         return this.create.get('patronBarcode');
192     }
193
194     get resourceListSelection() {
195       return this.create.get('resourceList').value;
196     }
197
198     get selectedTimes() {
199         return [this.create.get('startTime').value.toISOString(),
200             this.create.get('endTime').value.toISOString()];
201     }
202 }
203