From 6de5d92692e188a1142fd27927e482f09c0cec2d Mon Sep 17 00:00:00 2001 From: Jane Sandberg Date: Sun, 2 Jun 2019 15:21:45 -0700 Subject: [PATCH] LP1816475: Turning Create Reservation controls into an accordion, reactive form Signed-off-by: Jane Sandberg --- .../patron_barcode_validator.directive.ts | 47 ++++ .../eg2/src/app/staff/booking/booking.module.ts | 2 + .../create-reservation-dialog.component.html | 43 +++ .../booking/create-reservation-dialog.component.ts | 64 +++++ .../booking/create-reservation.component.html | 298 ++++++++++----------- .../staff/booking/create-reservation.component.ts | 104 ++----- .../booking/manage-reservations.component.html | 2 +- Open-ILS/src/eg2/src/app/staff/common.module.ts | 8 +- 8 files changed, 319 insertions(+), 249 deletions(-) create mode 100644 Open-ILS/src/eg2/src/app/share/validators/patron_barcode_validator.directive.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/booking/create-reservation-dialog.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/booking/create-reservation-dialog.component.ts diff --git a/Open-ILS/src/eg2/src/app/share/validators/patron_barcode_validator.directive.ts b/Open-ILS/src/eg2/src/app/share/validators/patron_barcode_validator.directive.ts new file mode 100644 index 0000000000..decbfc9189 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/share/validators/patron_barcode_validator.directive.ts @@ -0,0 +1,47 @@ +import { Directive, forwardRef } from '@angular/core'; +import { NG_VALIDATORS, NG_ASYNC_VALIDATORS, AbstractControl, ValidationErrors, AsyncValidator, FormControl } from '@angular/forms'; +import {NetService} from '@eg/core/net.service'; +import {AuthService} from '@eg/core/auth.service'; +import {Observable, of} from 'rxjs'; +import {single, switchMap, catchError} from 'rxjs/operators'; +import {Injectable} from '@angular/core'; + +@Injectable({providedIn: 'root'}) +export class PatronBarcodeValidator implements AsyncValidator { + constructor( + private auth : AuthService, + private net : NetService) { + } + + validate = (control: FormControl) => { + return this.net.request( + 'open-ils.actor', + 'open-ils.actor.get_barcodes', + this.auth.token(), + this.auth.user().ws_ou(), + 'actor', control.value) + .pipe(single(), + switchMap(() => of(null)), + catchError(() => of({ patronBarcode: 'No patron found with that barcode' }))); + } +} + +@Directive({ + selector: '[egValidPatronBarcode]', + providers: [{ + provide: NG_ASYNC_VALIDATORS, + useExisting: forwardRef(() => PatronBarcodeValidator), + multi: true + }] +}) +export class PatronBarcodeValidatorDirective { + constructor( + private pbv: PatronBarcodeValidator + ) { } + + validate = (control: FormControl) => { + this.pbv.validate(control); + } + +} + diff --git a/Open-ILS/src/eg2/src/app/staff/booking/booking.module.ts b/Open-ILS/src/eg2/src/app/staff/booking/booking.module.ts index df56a570bc..9ebce77c1e 100644 --- a/Open-ILS/src/eg2/src/app/staff/booking/booking.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/booking/booking.module.ts @@ -3,6 +3,7 @@ import {ReactiveFormsModule} from '@angular/forms'; import {StaffCommonModule} from '@eg/staff/common.module'; import {BookingRoutingModule} from './routing.module'; import {CreateReservationComponent} from './create-reservation.component'; +import {CreateReservationDialogComponent} from './create-reservation-dialog.component'; import {ManageReservationsComponent} from './manage-reservations.component'; import {OrgSelectWithDescendantsComponent} from './org-select-with-descendants.component'; import {ReservationsGridComponent} from './reservations-grid.component'; @@ -23,6 +24,7 @@ import {PatronService} from '@eg/staff/share/patron.service'; providers: [PatronService], declarations: [ CreateReservationComponent, + CreateReservationDialogComponent, ManageReservationsComponent, NoTimezoneSetComponent, OrgSelectWithDescendantsComponent, diff --git a/Open-ILS/src/eg2/src/app/staff/booking/create-reservation-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/booking/create-reservation-dialog.component.html new file mode 100644 index 0000000000..d1041779de --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/booking/create-reservation-dialog.component.html @@ -0,0 +1,43 @@ + + + + + diff --git a/Open-ILS/src/eg2/src/app/staff/booking/create-reservation-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/booking/create-reservation-dialog.component.ts new file mode 100644 index 0000000000..04c8ddf272 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/booking/create-reservation-dialog.component.ts @@ -0,0 +1,64 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {FormGroup, FormControl, Validators} from "@angular/forms"; +import {AuthService} from '@eg/core/auth.service'; +import {NetService} from '@eg/core/net.service'; +import {DialogComponent} from '@eg/share/dialog/dialog.component'; +import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; +import {PatronBarcodeValidator} from '@eg/share/validators/patron_barcode_validator.directive'; +import * as Moment from 'moment-timezone'; + +@Component({ + selector: 'eg-create-reservation-dialog', + templateUrl: './create-reservation-dialog.component.html' +}) + +export class CreateReservationDialogComponent + extends DialogComponent implements OnInit { + + constructor( + private auth: AuthService, + private net: NetService, + private modal: NgbModal, + private pbv: PatronBarcodeValidator + ) { + super(modal); + } + + create: FormGroup; + + addBresv: () => void; + + @Input() startTime: Moment; + @Input() endTime: Moment; + @Input() targetResource: number; + @Input() targetResourceBarcode: string; + @Input() attributes: any[]; + + ngOnInit() { + + this.create = new FormGroup({ + 'patronBarcode': new FormControl('', + [ Validators.required ], + [this.pbv.validate] + ), + 'emailNotify': new FormControl(true), + }); + + this.addBresv = () => { + this.net.request( + 'open-ils.booking', + 'open-ils.booking.reservations.create', + this.auth.token(), + '99999382659', + ["2019-09-09 10:00", "2019-09-09 14:00"], + 7, + 1, + this.targetResource ? this.targetResource : null, + [], + 0 + ); + } + + } +} + diff --git a/Open-ILS/src/eg2/src/app/staff/booking/create-reservation.component.html b/Open-ILS/src/eg2/src/app/staff/booking/create-reservation.component.html index b4d459b71a..3a542e1019 100644 --- a/Open-ILS/src/eg2/src/app/staff/booking/create-reservation.component.html +++ b/Open-ILS/src/eg2/src/app/staff/booking/create-reservation.component.html @@ -2,138 +2,158 @@ -
-
- - -
-
-
-
- -
-
- - -
- - +
+ + + + filter_list + Find resources + + +
+
+ + +
+
+
+
+ +
+ +
+
+
+
+
+ +
+ +
-
-
-
-
-
- -
- - -
-
-
-
-
- + + + + + + calendar_today + Select dates - () selected + + +
+
+
+
+ +
+
+ + +
+ + +
+
+
+
+
+
+
+ +
+ + +
+
- -
-
-
-
-
- + + + + + + edit_attributes + Advanced options + + +
+
+
Display options
+
    +
  • + + + + + + +
  • +
  • + + + + + + +
  • +
  • + + + + + + + + + + +
  • +
+
+
+
Filter by attributes
+
    +
  • + + + + + + + + + +
  • +
+
- -
-
-
-
- + + + + -
-
-
Display options
-
    -
  • - - - - - - -
  • -
  • - - - - - - -
  • -
  • - - - - - - - - - - -
  • -
-
-
-
Filter by attributes
-
    -
  • - - - - - - - - - -
  • -
-
-
- + - - + + @@ -144,51 +164,3 @@ - - - - - -
-
- - -
-
-
- - - - - - - - - -
Pickup library uses a different timezone than your library does. Please choose times in the pickup library's timezone.
-
- - - - - - - - - - - - - - - diff --git a/Open-ILS/src/eg2/src/app/staff/booking/create-reservation.component.ts b/Open-ILS/src/eg2/src/app/staff/booking/create-reservation.component.ts index c2fce8683c..7018d2f55c 100644 --- a/Open-ILS/src/eg2/src/app/staff/booking/create-reservation.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/booking/create-reservation.component.ts @@ -1,4 +1,5 @@ import {Component, Input, OnInit, AfterViewInit, QueryList, ViewChildren, ViewChild} from '@angular/core'; +import {FormGroup, FormControl} from "@angular/forms"; import {Router, ActivatedRoute, ParamMap} from '@angular/router'; import {forkJoin} from 'rxjs'; import {single} from 'rxjs/operators'; @@ -7,7 +8,6 @@ import {AuthService} from '@eg/core/auth.service'; import {ComboboxEntry} from '@eg/share/combobox/combobox.component'; import {DateSelectComponent} from '@eg/share/date-select/date-select.component'; import {DateRangeSelectComponent} from '@eg/share/daterange-select/daterange-select.component'; -import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component'; import {FormatService} from '@eg/core/format.service'; import {GridComponent} from '@eg/share/grid/grid.component'; import {GridDataSource, GridRowFlairEntry} from '@eg/share/grid/grid'; @@ -16,6 +16,7 @@ import {NetService} from '@eg/core/net.service'; import {OrgService} from '@eg/core/org.service'; import {PatronService} from '@eg/staff/share/patron.service'; import {PcrudService} from '@eg/core/pcrud.service'; +import {CreateReservationDialogComponent} from './create-reservation-dialog.component'; import {ResourceTypeComboboxComponent} from './resource-type-combobox.component'; import {ServerStoreService} from '@eg/core/server-store.service'; import {ToastService} from '@eg/share/toast/toast.service'; @@ -30,7 +31,8 @@ import * as Moment from 'moment-timezone'; export class CreateReservationComponent implements OnInit, AfterViewInit { - advancedCollapsed = true; + criteria: FormGroup; + attributes: IdlObject[] = []; selectedAttributes: number[] = []; multiday = false; @@ -60,12 +62,11 @@ export class CreateReservationComponent implements OnInit, AfterViewInit { minuteStep: () => number; - openCreateDialog: (rows: IdlObject[]) => void; openTheDialog: (rows: IdlObject[]) => any; resources: IdlObject[] = []; limitByAttr: (attributeId: number, $event: ComboboxEntry) => void; - useCurrentResourceBarcode: () => void; + useResourceBarcode: (barcode: string) => void; findPatronByBarcode: () => void; setGranularity: () => void; @@ -78,8 +79,8 @@ export class CreateReservationComponent implements OnInit, AfterViewInit { @ViewChildren('dateLimiter') dateLimiters: QueryList; @ViewChildren('dateRangeLimiter') dateRangeLimiters: QueryList; @ViewChildren('scheduleGrid') scheduleGrids: QueryList; - @ViewChild('newDialog') newDialog: FmRecordEditorComponent; @ViewChild('rt') rt: ResourceTypeComboboxComponent; + @ViewChild('createDialog') createDialog: CreateReservationDialogComponent; idealDate = new Date(); @@ -112,6 +113,18 @@ export class CreateReservationComponent implements OnInit, AfterViewInit { ngOnInit() { + this.criteria = new FormGroup({ + 'resourceBarcode': new FormControl(this.resourceBarcode ? this.resourceBarcode : ''), + 'startOfDay': new FormControl(this.startOfDay), + 'endOfDay': new FormControl(this.endOfDay), + }); + + this.criteria.valueChanges.subscribe(() => { this.fetchData(); }); + + this.criteria.get('resourceBarcode').valueChanges.subscribe((barcode) => { this.useResourceBarcode(barcode); }); + + this.criteria.get('resourceBarcode').valueChanges.subscribe((barcode) => { this.useResourceBarcode(barcode); }); + this.owningLibraries = [this.auth.user().ws_ou()]; this.defaultTimes = { @@ -215,7 +228,6 @@ export class CreateReservationComponent implements OnInit, AfterViewInit { }; this.handlePickupLibChange = ($event) => { - this.newDialog.record.pickup_lib($event); this.org.settings('lib.timezone', $event.id()).then((tz) => { if (tz['lib.timezone'] && (this.format.wsOrgTimezone !== tz['lib.timezone'])) { this.pickupLibUsesDifferentTz = tz['lib.timezone']; @@ -225,26 +237,9 @@ export class CreateReservationComponent implements OnInit, AfterViewInit { }); }; - this.handleTargetResourceChange = ($event) => { - if ('any' !== $event) { - this.newDialog.record.current_resource($event); - this.newDialog.record.target_resource($event); - } - }; - - this.useCurrentResourceBarcode = () => { - if (this.resourceBarcode) { - this.router.navigate(['/staff', 'booking', 'create_reservation', 'for_resource', this.resourceBarcode]); - } - }; - this.findPatronByBarcode = () => { - if (this.patronBarcode) { - this.patron.bcSearch(this.patronBarcode).pipe(single()).subscribe( - resp => { this.newDialog.record.usr(resp[0].id); }, - err => { this.toast.danger('No patron found with this barcode'); }, - ); - } + this.useResourceBarcode = (barcode) => { + this.router.navigate(['/staff', 'booking', 'create_reservation', 'for_resource', barcode]); }; this.minuteStep = () => { @@ -258,7 +253,7 @@ export class CreateReservationComponent implements OnInit, AfterViewInit { this.fetchData(); this.openTheDialog = (rows: IdlObject[]) => { - return this.newDialog.open({size: 'lg'}).subscribe( + return this.createDialog.open({size: 'lg'}).subscribe( response => { this.toast.success('Reservation successfully created'); // TODO: needs i18n, pluralization this.fetchData(); @@ -267,63 +262,6 @@ export class CreateReservationComponent implements OnInit, AfterViewInit { ); }; - this.openCreateDialog = (rows: IdlObject[]) => { - if (rows.length) { - if (this.multiday) { - this.defaultTimes['start_time'] = this.format.momentizeDateString(rows[0]['time'], this.format.wsOrgTimezone); - this.defaultTimes['end_time'] = this.format.momentizeDateString( - rows[rows.length - 1]['time'], this.format.wsOrgTimezone).clone() - .add(this.granularity, 'minutes'); - } else { - this.defaultTimes['start_time'] = Moment.tz('' + - this.idealDate.getFullYear() + '-' + - (this.idealDate.getMonth() + 1) + '-' + - (this.idealDate.getDate()) + ' ' + rows[0]['time'], - 'YYYY-MM-DD LT', this.format.wsOrgTimezone); - this.defaultTimes['end_time'] = Moment.tz('' + - this.idealDate.getFullYear() + '-' + - (this.idealDate.getMonth() + 1) + '-' + - (this.idealDate.getDate()) + ' ' + rows[rows.length - 1]['time'], - 'YYYY-MM-DD LT', this.format.wsOrgTimezone).clone().add(this.granularity, 'minutes'); - } - } else { - if (this.multiday) { this.defaultTimes['end_time'] = this.defaultTimes['start_time'].clone().add(1, 'days'); } - } - if (this.resourceId && !this.resourceTypeId) { - this.pcrud.search('brsrc', {id: this.resourceId}, { - flesh: 1, - limit: 1, - flesh_fields: {'brsrc': ['type']} - }).subscribe( r => { - this.transferable = r.type().transferable(); - this.resourceTypeId = r.type().id(); - this.resourceOwner = r.owner(); - this.openTheDialog(rows); - }); - } else if (this.resourceTypeId) { - this.pcrud.search('brt', {id: this.resourceTypeId}, { - }).subscribe( t => { - this.transferable = t.transferable(); - this.openTheDialog(rows).then(newId => { - if (this.selectedAttributes.length) { - const creates$ = []; - this.selectedAttributes.forEach(attrValue => { - if (attrValue) { - const bravm = this.idl.create('bravm'); - bravm.attr_value(attrValue); - bravm.reservation(newId); - creates$.push(this.pcrud.create(bravm)); - } - }); - forkJoin(...creates$).subscribe(() => { - this.net.request('open-ils.storage', 'open-ils.storage.booking.reservation.resource_targeter', [newId]); }); - } else { - this.net.request('open-ils.storage', 'open-ils.storage.booking.reservation.resource_targeter', [newId]); - } - }); - }); - } - }; } handleResourceTypeChange($event: ComboboxEntry) { this.resourceBarcode = null; diff --git a/Open-ILS/src/eg2/src/app/staff/booking/manage-reservations.component.html b/Open-ILS/src/eg2/src/app/staff/booking/manage-reservations.component.html index 9926147907..c77b28c356 100644 --- a/Open-ILS/src/eg2/src/app/staff/booking/manage-reservations.component.html +++ b/Open-ILS/src/eg2/src/app/staff/booking/manage-reservations.component.html @@ -21,7 +21,7 @@
- +
diff --git a/Open-ILS/src/eg2/src/app/staff/common.module.ts b/Open-ILS/src/eg2/src/app/staff/common.module.ts index 65f29eb287..899d5b1e11 100644 --- a/Open-ILS/src/eg2/src/app/staff/common.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/common.module.ts @@ -24,6 +24,8 @@ import {BibSummaryComponent} from '@eg/staff/share/bib-summary/bib-summary.compo import {TranslateComponent} from '@eg/staff/share/translate/translate.component'; import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.component'; +import {PatronBarcodeValidatorDirective} from '@eg/share/validators/patron_barcode_validator.directive'; + /** * Imports the EG common modules and adds modules common to all staff UI's. */ @@ -47,7 +49,8 @@ import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.componen DateTimeSelectComponent, BibSummaryComponent, TranslateComponent, - AdminPageComponent + AdminPageComponent, + PatronBarcodeValidatorDirective ], imports: [ EgCommonModule, @@ -73,7 +76,8 @@ import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.componen DateTimeSelectComponent, BibSummaryComponent, TranslateComponent, - AdminPageComponent + AdminPageComponent, + PatronBarcodeValidatorDirective ] }) -- 2.11.0