--- /dev/null
+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);
+ }
+
+}
+
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';
providers: [PatronService],
declarations: [
CreateReservationComponent,
+ CreateReservationDialogComponent,
ManageReservationsComponent,
NoTimezoneSetComponent,
OrgSelectWithDescendantsComponent,
--- /dev/null
+<ng-template #dialogContent>
+ <div class="modal-header bg-info">
+ <h3 class="modal-title" i18n>Confirm Reservation Details</h3>
+ <button type="button" class="close"
+ i18n-aria-label aria-label="Close"
+ (click)="dismiss('cross_click')">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <form class="modal-body form-common" [formGroup]="create">
+ <div class="form-group row">
+ <label class="col-lg-4 text-right font-weight-bold"
+ i18n for="create-patron-barcode">Patron barcode</label>
+ <input type="text" id="create-patron-barcode"
+ class="form-control col-lg-7" formControlName="patronBarcode">
+ </div>
+ <div class="form-group row">
+ <label class="col-lg-4 text-right font-weight-bold"
+ i18n for="create-end-time">Start time</label>
+ <eg-datetime-select></eg-datetime-select>
+ </div>
+ <div class="form-group row">
+ <label class="col-lg-4 text-right font-weight-bold"
+ i18n for="create-end-time">End time</label>
+ <eg-datetime-select></eg-datetime-select>
+ </div>
+ <div class="form-group row">
+ <label class="col-lg-4 text-right font-weight-bold"
+ i18n for="create-resource">Resource</label>
+ <input *ngIf="targetResource && targetResourceBarcode" id="create-resource" value="{{targetResourceBarcode}}" disabled>
+ <eg-combobox *ngIf="!(targetResource && targetResourceBarcode)"></eg-combobox>
+ </div>
+ <div class="form-group row">
+ <label class="col-lg-4 text-right font-weight-bold"
+ i18n for="create-email-notify">Notify by email?</label>
+ <input type="checkbox" formControlName="emailNotify">
+ </div>
+ </form>
+ <div class="modal-footer">
+ <button (click)="addBresv()" class="btn btn-info" i18n>Confirm reservation</button>
+ <button (click)="close()" class="btn btn-warning ml-2" i18n>Cancel</button>
+ </div>
+</ng-template>
--- /dev/null
+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
+ );
+ }
+
+ }
+}
+
</eg-staff-banner>
<eg-title i18n-prefix i18n-suffix prefix="Booking" suffix="Create Reservation"></eg-title>
-<div class="row">
- <div class="col">
- <eg-org-select-with-descendants labelText="Owning library" i18n-labelText (ouChange)="handleOwnerChange($event)">
- </eg-org-select-with-descendants>
- </div>
- <div class="col">
- <div class="input-group">
- <div class="input-group-prepend">
- <label class="input-group-text" for="ideal-reservation-type" i18n>Reservation type</label>
- </div>
- <div ngbDropdown>
- <button *ngIf="!multiday" class="btn btn-outline-primary" ngbDropdownToggle><span class="material-icons">event</span><span i18n>Single day reservation</span></button>
- <button *ngIf="multiday" class="btn btn-outline-primary" ngbDropdownToggle><span class="material-icons">date_range</span><span i18n>Multiple day reservation</span></button>
- <div ngbDropdownMenu id="ideal-reservation-type">
- <button (click)="handleSingleDayReservation()" class="btn btn-outline-primary" ngbDropdownItem><span class="material-icons">event</span><span i18n>Single day reservation</span></button>
- <button (click)="handleMultiDayReservation()" class="btn btn-outline-primary" ngbDropdownItem><span class="material-icons">date_range</span><span i18n>Multiple day reservation</span></button>
+<form [formGroup]="criteria">
+<ngb-accordion [closeOthers]="true" activeIds="select-resources">
+ <ngb-panel id="select-resources">
+ <ng-template ngbPanelTitle>
+ <span class="material-icons">filter_list</span>
+ <ng-container i18n>Find resources</ng-container>
+ </ng-template>
+ <ng-template ngbPanelContent>
+ <div ngbPanelContent class="row">
+ <div class="col">
+ <eg-org-select-with-descendants labelText="Owning library" i18n-labelText (ouChange)="handleOwnerChange($event)">
+ </eg-org-select-with-descendants>
+ </div>
+ <div class="col">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <label class="input-group-text" for="ideal-resource-barcode" i18n>Search by resource barcode</label>
+ </div>
+ <input type="text" id="ideal-resource-barcode" class="form-control" i18n-placeholder placeholder="Resource barcode" formControlName="resourceBarcode">
+ </div>
+ </div>
+ <div class="col">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <label class="input-group-text" for="ideal-resource-type" i18n>Search by resource type</label>
+ </div>
+ <eg-resource-type-combobox #rt domId="ideal-resource-type" (typeChanged)="handleResourceTypeChange($event)"></eg-resource-type-combobox>
+ </div>
</div>
</div>
- </div>
- </div>
- <div class="col">
- <div class="input-group">
- <div class="input-group-prepend">
- <label class="input-group-text" for="ideal-reservation-date" i18n>Reservation date</label>
- </div>
- <eg-date-select *ngIf="!multiday" #dateLimiter domId="ideal-reservation-date" (onChangeAsDate)="handleDateChange($event)" [initialDate]="idealDate"></eg-date-select>
- <eg-daterange-select *ngIf="multiday" #dateRangeLimiter (onChange)="fetchData()"></eg-daterange-select>
- </div>
- </div>
- <div class="col">
- <div class="input-group">
- <div class="input-group-prepend">
- <label class="input-group-text" for="ideal-resource-barcode" i18n>Resource barcode</label>
+ </ng-template>
+ </ngb-panel>
+
+ <ngb-panel id="select-dates">
+ <ng-template ngbPanelTitle>
+ <span class="material-icons">calendar_today</span>
+ <ng-container i18n>Select dates - () selected</ng-container>
+ </ng-template>
+ <ng-template ngbPanelContent>
+ <div ngbPanelContent class="row">
+ <div class="col">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <label class="input-group-text" for="ideal-reservation-type" i18n>Reservation type</label>
+ </div>
+ <div ngbDropdown>
+ <button *ngIf="!multiday" class="btn btn-outline-primary" ngbDropdownToggle><span class="material-icons">event</span><span i18n>Single day reservation</span></button>
+ <button *ngIf="multiday" class="btn btn-outline-primary" ngbDropdownToggle><span class="material-icons">date_range</span><span i18n>Multiple day reservation</span></button>
+ <div ngbDropdownMenu id="ideal-reservation-type">
+ <button (click)="handleSingleDayReservation()" class="btn btn-outline-primary" ngbDropdownItem><span class="material-icons">event</span><span i18n>Single day reservation</span></button>
+ <button (click)="handleMultiDayReservation()" class="btn btn-outline-primary" ngbDropdownItem><span class="material-icons">date_range</span><span i18n>Multiple day reservation</span></button>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <label class="input-group-text" for="ideal-reservation-date" i18n>Reservation date</label>
+ </div>
+ <eg-date-select *ngIf="!multiday" #dateLimiter domId="ideal-reservation-date" (onChangeAsDate)="handleDateChange($event)" [initialDate]="idealDate"></eg-date-select>
+ <eg-daterange-select *ngIf="multiday" #dateRangeLimiter (onChange)="fetchData()"></eg-daterange-select>
+ </div>
+ </div>
</div>
- <input type="text" id="ideal-resource-barcode" class="form-control" i18n-placeholder placeholder="Resource barcode" [(ngModel)]="resourceBarcode" (change)="useCurrentResourceBarcode()">
- </div>
- </div>
- <div class="col">
- <div class="input-group">
- <div class="input-group-prepend">
- <label class="input-group-text" for="ideal-resource-type" i18n>Resource type</label>
+ </ng-template>
+ </ngb-panel>
+
+ <ngb-panel id="advanced">
+ <ng-template ngbPanelTitle>
+ <span class="material-icons">edit_attributes</span>
+ <ng-container i18n>Advanced options</ng-container>
+ </ng-template>
+ <ng-template ngbPanelContent>
+ <div class="row">
+ <div class="card col-md-6">
+ <div class="card-header" i18n>Display options</div>
+ <ul class="list-group list-group-flush">
+ <li class="list-group-item">
+ <span class="input-group">
+ <span class="input-group-prepend">
+ <label class="input-group-text" for="start-time" i18n>Start time</label>
+ </span>
+ <ngb-timepicker formControlName="startOfDay" [minuteStep]="minuteStep()" [meridian]="true"></ngb-timepicker>
+ </span>
+ </li>
+ <li class="list-group-item">
+ <span class="input-group">
+ <span class="input-group-prepend">
+ <label class="input-group-text" for="end-time" i18n>End time</label>
+ </span>
+ <ngb-timepicker formControlName="endOfDay" [minuteStep]="minuteStep()" [meridian]="true"></ngb-timepicker>
+ </span>
+ </li>
+ <li class="list-group-item">
+ <span class="input-group">
+ <span class="input-group-prepend">
+ <label class="input-group-text" for="granularity" i18n>Granularity</label>
+ </span>
+ <eg-combobox (onChange)="changeGranularity($event)" [startId]="granularity ? granularity : 30">
+ <eg-combobox-entry entryId="15" entryLabel="15 minutes"
+ i18n-entryLabel></eg-combobox-entry>
+ <eg-combobox-entry entryId="30" entryLabel="30 minutes"
+ i18n-entryLabel></eg-combobox-entry>
+ <eg-combobox-entry entryId="60" entryLabel="60 minutes"
+ i18n-entryLabel></eg-combobox-entry>
+ </eg-combobox>
+ </span>
+ </li>
+ </ul>
+ </div>
+ <div *ngIf="attributes.length" class="card col-md-6">
+ <div class="card-header" i18n>Filter by attributes</div>
+ <ul class="list-group list-group-flush">
+ <li *ngFor="let attribute of attributes" class="list-group-item">
+ <span class="input-group">
+ <span class="input-group-prepend">
+ <label class="input-group-text" for="attribute-{{attribute.id()}}" i18n>{{attribute.name()}}</label>
+ </span>
+ <eg-combobox (onChange)="limitByAttr(attribute.id(), $event)">
+ <eg-combobox-entry *ngFor="let value of attribute.valid_values()"
+ [entryId]="value.id()" [entryLabel]="value.valid_value()">
+ </eg-combobox-entry>
+ </eg-combobox>
+ </span>
+ </li>
+ </ul>
+ </div>
</div>
- <eg-resource-type-combobox #rt domId="ideal-resource-type" (typeChanged)="handleResourceTypeChange($event)"></eg-resource-type-combobox>
- </div>
- </div>
-</div>
-<hr class="mt1" />
-<button
- class="btn btn-primary"
- (click)="advancedCollapsed = !advancedCollapsed"
- [attr.aria-expanded]="!advancedCollapsed"
- aria-controls="advanced">
- <span *ngIf="advancedCollapsed" class="material-icons">lock</span>
- <span *ngIf="!advancedCollapsed" class="material-icons">lock_open</span>
- <span *ngIf="advancedCollapsed" i18n>Show advanced options</span>
- <span *ngIf="!advancedCollapsed" i18n>Hide advanced options</span>
-</button>
+ </ng-template>
+ </ngb-panel>
+</ngb-accordion>
+</form>
-<div id="advanced" class="row" [ngbCollapse]="advancedCollapsed">
- <div class="card col-md-6">
- <div class="card-header" i18n>Display options</div>
- <ul class="list-group list-group-flush">
- <li class="list-group-item">
- <span class="input-group">
- <span class="input-group-prepend">
- <label class="input-group-text" for="start-time" i18n>Start time</label>
- </span>
- <ngb-timepicker [(ngModel)]="startOfDay" (ngModelChange)="fetchData()" [minuteStep]="minuteStep()" [meridian]="true"></ngb-timepicker>
- </span>
- </li>
- <li class="list-group-item">
- <span class="input-group">
- <span class="input-group-prepend">
- <label class="input-group-text" for="end-time" i18n>End time</label>
- </span>
- <ngb-timepicker [(ngModel)]="endOfDay" (ngModelChange)="fetchData()" [minuteStep]="minuteStep()" [meridian]="true"></ngb-timepicker>
- </span>
- </li>
- <li class="list-group-item">
- <span class="input-group">
- <span class="input-group-prepend">
- <label class="input-group-text" for="granularity" i18n>Granularity</label>
- </span>
- <eg-combobox (onChange)="changeGranularity($event)" [startId]="granularity ? granularity : 30">
- <eg-combobox-entry entryId="15" entryLabel="15 minutes"
- i18n-entryLabel></eg-combobox-entry>
- <eg-combobox-entry entryId="30" entryLabel="30 minutes"
- i18n-entryLabel></eg-combobox-entry>
- <eg-combobox-entry entryId="60" entryLabel="60 minutes"
- i18n-entryLabel></eg-combobox-entry>
- </eg-combobox>
- </span>
- </li>
- </ul>
- </div>
- <div *ngIf="attributes.length" class="card col-md-6">
- <div class="card-header" i18n>Filter by attributes</div>
- <ul class="list-group list-group-flush">
- <li *ngFor="let attribute of attributes" class="list-group-item">
- <span class="input-group">
- <span class="input-group-prepend">
- <label class="input-group-text" for="attribute-{{attribute.id()}}" i18n>{{attribute.name()}}</label>
- </span>
- <eg-combobox (onChange)="limitByAttr(attribute.id(), $event)">
- <eg-combobox-entry *ngFor="let value of attribute.valid_values()"
- [entryId]="value.id()" [entryLabel]="value.valid_value()">
- </eg-combobox-entry>
- </eg-combobox>
- </span>
- </li>
- </ul>
- </div>
-</div>
<eg-grid *ngIf="resources.length" #scheduleGrid
[sortable]="false"
- (onRowActivate)="openCreateDialog([$event])"
+ (onRowActivate)="openTheDialog([$event])"
[dataSource]="scheduleSource"
[rowFlairIsEnabled]="true"
[rowFlairCallback]="resourceAvailabilityIcon"
[disablePaging]="true"
persistKey="disabled">
- <eg-grid-toolbar-action label="Create Reservation" i18n-label [action]="openCreateDialog"></eg-grid-toolbar-action>
+ <eg-grid-toolbar-action label="Create Reservation" i18n-label (onClick)="openTheDialog()"></eg-grid-toolbar-action>
<eg-grid-column path="time" [index]="true" ></eg-grid-column>
<eg-grid-column *ngFor="let resource of resources" path="{{resource.barcode()}}" [cellTemplate]="reservationsTemplate" [disableTooltip]="true"></eg-grid-column>
</eg-grid>
-<eg-fm-record-editor #newDialog
- idlClass="bresv"
- [fieldOptions]="{usr:{customTemplate:{template:patronTemplate}},start_time:{customTemplate:{template:datetimeWithDefaults}},end_time:{customTemplate:{template:datetimeWithDefaults}},pickup_lib:{customTemplate:{template:pickupLibrary}},target_resource:{customTemplate:{template:targetResource}}}"
- hiddenFields="id,xact_start,request_time,capture_time,pickup_time,return_time,capture_staff,xact_finish,cancel_time,booking_interval,unrecovered,request_lib,fine_interval,fine_amount,max_fine,current_resource,target_resource_type">
-</eg-fm-record-editor>
+<eg-create-reservation-dialog #createDialog
+ [targetResourceBarcode]="resourceBarcode"
+ [targetResource]="resourceId">
+</eg-create-reservation-dialog>
<ng-template #reservationsTemplate let-row="row" let-col="col">
<ng-container *ngIf="row[col.name]">
</ul>
</ng-container>
</ng-template>
-<ng-template #patronTemplate let-record="record">
-<input type="hidden" value="{{record.request_lib(auth.user().ws_ou())}}">
- <ng-container *ngIf="patronId">
- <input *ngIf="patronId" type="text" disabled value="{{record.usr(patronId)}}" class="form-control" name="usr">
- </ng-container>
- <div *ngIf="!patronId" class="input-group flex-nowrap">
- <div class="input-group-prepend">
- <label class="input-group-text" for="patron-barcode" i18n>Patron barcode</label>
- <input type="text" id="patron-barcode" class="form-control" i18n-placeholder placeholder="Patron barcode" [(ngModel)]="patronBarcode" (change)="findPatronByBarcode()">
- </div>
- </div>
-</ng-template>
-<ng-template #datetimeWithDefaults let-record="record" let-field="field">
- <input type="hidden" value="{{record[field.name](defaultTimes[field.name].toISOString())}}">
- <eg-datetime-select
- [showTZ]="true"
- [minuteStep]="minuteStep()"
- [timezone]="pickupLibUsesDifferentTz ? pickupLibUsesDifferentTz : format.wsOrgTimezone"
- (onChangeAsIso)="record[field.name]($event)"
- (onChangeAsMoment)="field.validatorError = reservationValidate[field.name](field.name, $event, record)"
- [validatorError]="field.validatorError"
- [initialMoment]="defaultTimes[field.name]">
- </eg-datetime-select>
-</ng-template>
-<ng-template #pickupLibrary let-record="record" let-field="field">
- <input type="hidden" value="{{record.pickup_lib(auth.user().ws_ou())}}">
- <eg-org-select
- [initialOrgId]="auth.user().ws_ou()"
- (onChange)="handlePickupLibChange($event)">
- </eg-org-select>
- <div *ngIf="pickupLibUsesDifferentTz" class="alert alert-primary" i18n>Pickup library uses a different timezone than your library does. Please choose times in the pickup library's timezone.</div>
-</ng-template>
-<ng-template #targetResource let-record="record">
- <input type="hidden" value="{{record.target_resource_type(resourceTypeId)}}">
- <ng-container *ngIf="resourceId">
- <input type="text" disabled value="{{resourceBarcode}}" class="form-control">
- <input type="hidden" value="{{record.target_resource(resourceId)}}">
- <input type="hidden" value="{{record.current_resource(resourceId)}}">
- </ng-container>
- <ng-container *ngIf="!resourceId">
- <eg-combobox (onChange)="handleTargetResourceChange($event.id)" startId="any">
- <eg-combobox-entry entryId="any" entryLabel="Any resource"
- i18n-entryLabel></eg-combobox-entry>
- <eg-combobox-entry *ngFor="let r of resources" entryId="{{r.id()}}" entryLabel="{{r.barcode()}}">
- </eg-combobox-entry>
- </eg-combobox>
- </ng-container>
-</ng-template>
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';
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';
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';
export class CreateReservationComponent implements OnInit, AfterViewInit {
- advancedCollapsed = true;
+ criteria: FormGroup;
+
attributes: IdlObject[] = [];
selectedAttributes: number[] = [];
multiday = false;
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;
@ViewChildren('dateLimiter') dateLimiters: QueryList<DateSelectComponent>;
@ViewChildren('dateRangeLimiter') dateRangeLimiters: QueryList<DateRangeSelectComponent>;
@ViewChildren('scheduleGrid') scheduleGrids: QueryList<GridComponent>;
- @ViewChild('newDialog') newDialog: FmRecordEditorComponent;
@ViewChild('rt') rt: ResourceTypeComboboxComponent;
+ @ViewChild('createDialog') createDialog: CreateReservationDialogComponent;
idealDate = new Date();
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 = {
};
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'];
});
};
- 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 = () => {
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();
);
};
- 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;
<div class="input-group-prepend">
<label class="input-group-text" for="patron-barcode-value" i18n>Patron barcode</label>
</div>
- <input type="text" id="patron-barcode-value" class="form-control" i18n-placeholder placeholder="Patron barcode" [(ngModel)]="patronBarcode" (change)="filterByCurrentPatronBarcode()">
+ <input type="text" id="patron-barcode-value" class="form-control" i18n-placeholder placeholder="Patron barcode" egValidPatronBarcode [(ngModel)]="patronBarcode" (change)="filterByCurrentPatronBarcode()">
<div class="input-group-button">
<button *ngIf="patronBarcode" class="btn btn-warning" (click)="removeFilters()" i18n><span class="material-icons">delete</span> Remove filter</button>
</div>
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.
*/
DateTimeSelectComponent,
BibSummaryComponent,
TranslateComponent,
- AdminPageComponent
+ AdminPageComponent,
+ PatronBarcodeValidatorDirective
],
imports: [
EgCommonModule,
DateTimeSelectComponent,
BibSummaryComponent,
TranslateComponent,
- AdminPageComponent
+ AdminPageComponent,
+ PatronBarcodeValidatorDirective
]
})