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';
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 &&
26 : { startTimeNotBeforeEndTime: true };
30 selector: 'eg-create-reservation-dialog',
31 templateUrl: './create-reservation-dialog.component.html'
34 export class CreateReservationDialogComponent
35 extends DialogComponent implements OnInit {
37 @Input() targetResource: number;
38 @Input() targetResourceBarcode: string;
39 @Input() targetResourceType: ComboboxEntry;
40 @Input() patronId: number;
41 @Input() attributes: number[] = [];
42 @Input() resources: IdlObject[] = [];
43 @Output() onComplete: EventEmitter<boolean>;
46 patron$: Observable<{first_given_name: string, second_given_name: string, family_name: string}>;
48 timezone: string = this.format.wsOrgTimezone;
49 pickupLibraryUsesDifferentTz: boolean;
51 public disableOrgs: () => number[];
52 addBresv$: () => Observable<any>;
53 @ViewChild('fail', { static: true }) private fail: AlertDialogComponent;
55 handlePickupLibChange: ($event: IdlObject) => void;
58 private auth: AuthService,
59 private format: FormatService,
60 private net: NetService,
61 private org: OrgService,
62 private pcrud: PcrudService,
63 private router: Router,
64 private modal: NgbModal,
65 private pbv: PatronBarcodeValidator,
66 private toast: ToastService
69 this.onComplete = new EventEmitter<boolean>();
74 this.create = new FormGroup({
75 // TODO: replace this control with a patron search form
76 // when available in the Angular client
77 'patronBarcode': new FormControl('',
78 [Validators.required],
81 'emailNotify': new FormControl(true),
82 'startTime': new FormControl(),
83 'endTime': new FormControl(),
84 'resourceList': new FormControl(),
85 }, [startTimeIsBeforeEndTimeValidator]
88 this.pcrud.search('au', {id: this.patronId}, {
90 flesh_fields: {'au': ['card']}
92 this.create.patchValue({patronBarcode: usr.card().barcode()})
96 this.addBresv$ = () => {
97 let selectedResourceId = this.targetResource ? [this.targetResource] : null;
98 if (!selectedResourceId &&
99 this.resourceListSelection !== null &&
100 'any' !== this.resourceListSelection.id) {
101 selectedResourceId = [this.resourceListSelection.id];
103 return this.net.request(
105 'open-ils.booking.reservations.create',
107 this.patronBarcode.value,
110 this.targetResourceType.id,
112 this.attributes.filter(Boolean),
116 if (success.ilsevent) {
117 console.warn(success);
120 this.toast.success('Reservation successfully created');
121 console.debug(success);
127 }, () => this.onComplete.emit(true)
131 this.handlePickupLibChange = ($event) => {
132 this.pickupLibId = $event.id();
133 this.org.settings('lib.timezone', this.pickupLibId).then((tz) => {
134 this.timezone = tz['lib.timezone'] || this.format.wsOrgTimezone;
135 this.pickupLibraryUsesDifferentTz = (tz['lib.timezone'] && (this.format.wsOrgTimezone !== tz['lib.timezone']));
139 this.disableOrgs = () => this.org.filterList( { canHaveVolumes : false }, true);
141 this.patron$ = this.patronBarcode.statusChanges.pipe(
142 startWith({first_given_name: '', second_given_name: '', family_name: ''}),
144 if ('VALID' === this.patronBarcode.status) {
145 return this.net.request(
147 'open-ils.actor.get_barcodes',
149 this.auth.user().ws_ou(),
150 'actor', this.patronBarcode.value).pipe(
152 switchMap((result) => {
153 return this.pcrud.retrieve('au', result[0]['id']).pipe(
156 first_given_name: au.first_given_name(),
157 second_given_name: au.second_given_name(),
158 family_name: au.family_name()});
165 first_given_name: '',
166 second_given_name: '',
174 setDefaultTimes(times: Moment[], granularity: number) {
175 this.create.patchValue({startTime: Moment.min(times),
176 endTime: Moment.max(times).clone().add(granularity, 'minutes')
180 openPatronReservations = (): void => {
183 'open-ils.actor.get_barcodes',
185 this.auth.user().ws_ou(),
186 'actor', this.patronBarcode.value
187 ).subscribe((patron) => this.router.navigate(['/staff', 'booking', 'manage_reservations', 'by_patron', patron[0]['id']]));
190 addBresvAndOpenPatronReservations = (): void => {
192 .subscribe(() => this.openPatronReservations());
196 return this.create.get('emailNotify').value;
199 get patronBarcode() {
200 return this.create.get('patronBarcode');
203 get resourceListSelection() {
204 return this.create.get('resourceList').value;
207 get selectedTimes() {
208 return [this.create.get('startTime').value.toISOString(),
209 this.create.get('endTime').value.toISOString()];