momentizeDateString(date: string, timezone: string): Moment {
const parseableFormat = this.makeFormatParseable(this.dateFormat);
- if (parseableFormat.length) {return Moment(date, parseableFormat, timezone)};
- return Moment(date, timezone);
+ if (parseableFormat.length) { return Moment.tz(date, parseableFormat, timezone); }
+ // TODO: The following fallback returns the date at midnight UTC,
+ // rather than midnight in the local TZ
+ return Moment.tz(date, timezone);
}
momentizeDateTimeString(datetime: string, timezone: string): Moment {
const parseableFormat = this.makeFormatParseable(this.dateTimeFormat);
- if (parseableFormat.length) {return Moment(datetime, parseableFormat, timezone)};
+ if (parseableFormat.length) { return Moment(datetime, parseableFormat, timezone); }
return Moment(datetime, timezone);
}
+<span class="material-icons" *ngIf="validatorError">error</span>
<div class="input-group">
<input type="datetime"
[attr.id]="domId.length ? domId : null"
Cannot edit this date or time.
</div>
<div *ngIf="!readOnly">
+ <div *ngIf="validatorError" class="alert alert-danger">
+ <span class="material-icons">error</span>
+ {{validatorError}}
+ </div>
<ngb-datepicker
[(ngModel)]="dateModel"
(ngModelChange)="modelChanged()"
@Input() domId = '';
@Input() fieldName: string;
@Input() required: boolean;
- @Input() minuteStep: number = 15;
+ @Input() minuteStep = 15;
@Input() showTZ = true;
@Input() timezone: string = this.format.wsOrgTimezone;
@Input() readOnly = false;
+ @Input() validatorError = '';
@Input() initialIso: string;
- @Output() onChangeAsIso = new EventEmitter();
+ @Output() onChangeAsIso = new EventEmitter<string>();
+ @Output() onChangeAsMoment = new EventEmitter<Moment>();
stringVersion: any; // Used internally on internal input
timeModel: NgbTimeStruct;
}
if (newDate && !isNaN(newDate)) {
- console.log('newDate');
// Set component view value
- this.stringVersion = this.format.transform({value: newDate, datatype: 'timestamp', datePlusTime: true});
+ this.stringVersion = this.format.transform({value: newDate, datatype: 'timestamp', datePlusTime: true});
// Update form passed in view value
+ this.onChangeAsMoment.emit(newDate);
this.onChangeAsIso.emit(newDate.toISOString());
}
}
[showTZ]="timezone"
[timezone]="timezone"
domId="{{idPrefix}}-{{field.name}}"
- (onChangeAsIso)="record[field.name]($event)"
+ (onChangeAsMoment)="record[field.name]($event)"
+ [validatorError]="field.validatorError"
[readOnly]="field.readOnly"
initialIso="{{record[field.name]()}}">
</eg-datetime-select>
// This supersedes all other isRequired specifiers.
isRequiredOverride?: (field: string, record: IdlObject) => boolean;
+ // If this function is defined, the function will be called
+ // when fields change their values, to check if users are entering
+ // valid values, and delivering an error message if not.
+ //
+ // Currently only implemented for the datetime-select widget
+ validator?: (field: string, value: any, record: IdlObject) => string;
+
// Directly apply the readonly status of the field.
// This only has an affect if the value is true.
isReadonly?: boolean;
|| fieldOptions.isReadonly === true
|| this.readonlyFieldsList.includes(field.name);
+ if (fieldOptions.validator) {
+ field.validator = fieldOptions.validator;
+ } else {
+ field.validator = (fieldName: string, value: any, record: IdlObject) => { return ''; }
+ }
+
+ field.validate = (fieldName: string, value: any, record: IdlObject) => {
+ field.validatorError = field.validator(fieldName, value, record); }
+
+
if (fieldOptions.isRequiredOverride) {
field.isRequired = () => {
return fieldOptions.isRequiredOverride(field.name, this.record);
// datatype == text / interval / editable-pkey
return 'text';
}
+
}
<input class="form-check-input" type="checkbox" [checked]="onlyShowCaptured" id="only-show-captured" (change)="handleShowCapturedChange()">
<label class="form-check-label" for="only-show-captured" i18n>Show only captured resources</label>
</div>
- <eg-grid #readyGrid [dataSource]="readySource"
- (onRowActivate)="pickup($event).subscribe()"
- [sortable]="true" persistKey="booking.pull_list" >
- <eg-grid-toolbar-action label="Pick up" i18n-label [action]="pickupSelected" [disableOnRows]="noSelectedRows"></eg-grid-toolbar-action>
- <eg-grid-column name="id" [hidden]="true" [index]="true" i18n-label label="ID" path="id"></eg-grid-column>
- <eg-grid-column name="barcode" label="Barcode" i18n-label path="current_resource.barcode"></eg-grid-column>
- <eg-grid-column name="title" label="Title or name" i18n-label path="target_resource_type.name"></eg-grid-column>
- <eg-grid-column label="Reservation start time" [datePlusTime]="true" path="reservations.0.start_time" i18n-label></eg-grid-column>
- <eg-grid-column label="Reservation end time" [datePlusTime]="true" path="reservations.0.end_time" i18n-label></eg-grid-column>
-
- </eg-grid>
+ <eg-reservations-grid #readyGrid [patron]="patronId" status="pickupReady" [onlyCaptured]="onlyShowCaptured" persistSuffix="pickup.ready" (onPickup)="this.pickedUpGrid.reloadGrid()"></eg-reservations-grid>
<h2 class="text-center mt-2" i18n>Already picked up</h2>
<eg-reservations-grid #pickedUpGrid [patron]="patronId" status="pickedUp" persistSuffix="pickup.picked_up"></eg-reservations-grid>
import { Component, Input, OnInit, ViewChild } from '@angular/core';
-import { GridDataSource } from '@eg/share/grid/grid';
import { Pager } from '@eg/share/util/pager';
import {PatronService} from '@eg/staff/share/patron.service';
import {PcrudService} from '@eg/core/pcrud.service';
import {NetService} from '@eg/core/net.service';
import {Observable} from 'rxjs';
import {single, tap} from 'rxjs/operators';
-import {GridComponent} from '@eg/share/grid/grid.component';
import {ReservationsGridComponent} from './reservations-grid.component';
import {Router, ActivatedRoute, ParamMap} from '@angular/router';
import {ServerStoreService} from '@eg/core/server-store.service';
patronId: number;
retrievePatron: () => void;
- @ViewChild('readyGrid') readyGrid: GridComponent;
+ @ViewChild('readyGrid') readyGrid: ReservationsGridComponent;
@ViewChild('pickedUpGrid') pickedUpGrid: ReservationsGridComponent;
- public readySource: GridDataSource;
noSelectedRows: (rows: IdlObject[]) => boolean;
- pickupSelected: (reservations: IdlObject[]) => void;
- pickup: (reservation: IdlObject) => Observable<any>;
onlyShowCaptured = true;
handleShowCapturedChange: () => void;
private patron: PatronService,
private route: ActivatedRoute,
private router: Router,
- private store: ServerStoreService,
+ private store: ServerStoreService,
private toast: ToastService
) {
}
}).subscribe(
(resp) => {
this.patronBarcode = resp.card().barcode();
- this.readyGrid.reload();
+ this.readyGrid.reloadGrid();
this.pickedUpGrid.reloadGrid();
}, (err) => { console.debug(err); }
);
});
- this.readySource = new GridDataSource();
- this.readySource.getRows = (pager: Pager, sort: any[]) => {
- const orderBy: any = {};
- let where = {
- 'usr' : this.patronId,
- 'pickup_time' : null,
- 'start_time' : {'!=': null},
- 'cancel_time' : null
- };
- if (this.onlyShowCaptured) {
- where['capture_time'] = {'!=': null};
- }
-
- return this.pcrud.search('bresv', where, {
- order_by: orderBy,
- limit: pager.limit,
- offset: pager.offset,
- flesh: 1,
- flesh_fields: {'bresv' : [
- 'usr', 'capture_staff', 'target_resource', 'target_resource_type', 'current_resource', 'request_lib', 'pickup_lib'
- ] }
- });
-
- };
this.retrievePatron = () => {
if (this.patronBarcode) {
this.patron.bcSearch(this.patronBarcode).pipe(single()).subscribe(
);
}
};
- this.noSelectedRows = (rows: IdlObject[]) => (rows.length === 0);
-
- this.pickupSelected = (reservations: IdlObject[]) => {
- const pickupOne = (thing: IdlObject) => {
- if (!thing) { return; }
- this.pickup(thing).subscribe(
- () => pickupOne(reservations.shift()));
- };
- pickupOne(reservations.shift());
- };
- this.pickup = (reservation: IdlObject) => {
- return this.net.request(
- 'open-ils.circ',
- 'open-ils.circ.reservation.pickup',
- this.auth.token(),
- {'patron_barcode': this.patronBarcode, 'reservation': reservation})
- .pipe(tap(
- (success) => {
- this.readyGrid.reload();
- this.pickedUpGrid.reloadGrid(); },
- (error) => { console.debug(error); }
- ));
- };
this.store.getItem('eg.booking.pickup.ready.only_show_captured').then(onlyCaptured => {
if (onlyCaptured != null) { this.onlyShowCaptured = onlyCaptured; }
});
this.handleShowCapturedChange = () => {
this.onlyShowCaptured = !this.onlyShowCaptured;
- this.readyGrid.reload();
+ this.readyGrid.reloadGrid();
this.store.setItem('eg.booking.pickup.ready.only_show_captured', this.onlyShowCaptured);
};
<eg-grid #grid [dataSource]="gridSource"
- (onRowActivate)="showEditDialog($event)"
+ (onRowActivate)="handleRowActivate($event)"
[sortable]="true" persistKey="booking.{{persistSuffix}}" >
<eg-grid-toolbar-action label="Edit Selected" i18n-label [action]="editSelected" [disableOnRows]="noSelectedRows"></eg-grid-toolbar-action>
<eg-grid-toolbar-action label="Cancel Selected" i18n-label [action]="cancelSelected" [disableOnRows]="cancelNotAppropriate"></eg-grid-toolbar-action>
+ <eg-grid-toolbar-action label="Pick Up Selected" i18n-label [action]="pickupSelected" [disableOnRows]="pickupNotAppropriate"></eg-grid-toolbar-action>
<eg-grid-toolbar-action label="Return Selected" i18n-label [action]="returnSelected" [disableOnRows]="returnNotAppropriate"></eg-grid-toolbar-action>
<eg-grid-toolbar-action label="View Reservations for This Patron" i18n-label [action]="viewByPatron" [disableOnRows]="notOnePatronSelected"></eg-grid-toolbar-action>
<eg-grid-toolbar-action label="View Reservations for This Resource" i18n-label [action]="viewByResource" [disableOnRows]="notOneResourceSelected"></eg-grid-toolbar-action>
<eg-grid-column name="id" [hidden]="true" [index]="true" i18n-label label="ID" path="id"></eg-grid-column>
- <eg-grid-column label="Patron username" i18n-label path="usr.usrname" [sortable]="false"></eg-grid-column>
+ <eg-grid-column label="Patron username" [hidden]="true" i18n-label path="usr.usrname" [sortable]="false"></eg-grid-column>
<eg-grid-column label="Patron barcode" i18n-label path="usr.card.barcode" [sortable]="false"></eg-grid-column>
<eg-grid-column label="Patron first name" i18n-label path="usr.first_given_name"></eg-grid-column>
<eg-grid-column label="Patron middle name" i18n-label [hidden]="true" path="usr.second_given_name"></eg-grid-column>
idlClass="bresv"
datetimeFields="start_time,end_time"
hiddenFields="xact_finish,cancel_time,booking_interval"
+ [fieldOptions]="{start_time:{validator:startTimeShouldBeFuture}}"
[readonlyFields]="listReadOnlyFields()">
</eg-fm-record-editor>
<eg-confirm-dialog #confirmCancelReservationDialog
-import {Component, Input, OnInit, ViewChild} from '@angular/core';
+import {Component, EventEmitter, Input, Output, OnInit, ViewChild} from '@angular/core';
import {Observable} from 'rxjs';
import {tap} from 'rxjs/operators';
import {AuthService} from '@eg/core/auth.service';
@Input() patron: number;
@Input() resource: number;
@Input() resourceType: number;
- @Input() status: 'pickedUp' | 'returnedToday';
+ @Input() status: 'pickupReady' | 'pickedUp' | 'returnReady' | 'returnedToday';
@Input() persistSuffix: string;
+ @Input() onlyCaptured = false;
+
+ @Output() onPickup = new EventEmitter<IdlObject>();
gridSource: GridDataSource;
patronBarcode: string;
@ViewChild('noTimezoneSetDialog') noTimezoneSetDialog: NoTimezoneSetComponent;
editSelected: (rows: IdlObject[]) => void;
+ pickupSelected: (rows: IdlObject[]) => void;
+ pickupResource: (rows: IdlObject) => Observable<any>;
returnSelected: (rows: IdlObject[]) => void;
returnResource: (rows: IdlObject) => Observable<any>;
cancelSelected: (rows: IdlObject[]) => void;
filterByCurrentResourceBarcode: () => void;
listReadOnlyFields: () => string;
+ startTimeShouldBeFuture: (fieldName: string, value: Moment, record: IdlObject) => string;
+
+ handleRowActivate: (row: IdlObject) => void;
+
reloadGrid: () => void;
noSelectedRows: (rows: IdlObject[]) => boolean;
notOnePatronSelected: (rows: IdlObject[]) => boolean;
notOneResourceSelected: (rows: IdlObject[]) => boolean;
cancelNotAppropriate: (rows: IdlObject[]) => boolean;
+ pickupNotAppropriate: (rows: IdlObject[]) => boolean;
returnNotAppropriate: (rows: IdlObject[]) => boolean;
constructor(
this.noTimezoneSetDialog.open();
}
+
this.gridSource = new GridDataSource();
this.gridSource.getRows = (pager: Pager, sort: any[]) => {
if (this.resource) {
where['current_resource'] = this.resource;
}
+ if (this.onlyCaptured) {
+ where['capture_time'] = {'!=': null};
+ }
+
if (this.status) {
- if ('pickedUp' === this.status) {
+ if ('pickupReady' === this.status) {
+ where['pickup_time'] = null;
+ where['start_time'] = {'!=': null};
+ } else if ('pickedUp' === this.status || 'returnReady' === this.status) {
where['pickup_time'] = {'!=': null};
where['return_time'] = null;
} else if ('returnedToday' === this.status) {
'usr', 'capture_staff', 'target_resource', 'target_resource_type', 'current_resource', 'request_lib', 'pickup_lib'
], 'au': ['card'] }
}).pipe(tap((row) => {
- this.org.settings('lib.timezone', row['pickup_lib']()).then((tz) => {row['timezone'] = tz['lib.timezone']});
+ this.org.settings('lib.timezone', row['pickup_lib']()).then((tz) => {row['timezone'] = tz['lib.timezone'];});
}));
};
this.notOnePatronSelected = (rows: IdlObject[]) => (new Set(rows.map(row => row.usr().id())).size !== 1);
this.notOneResourceSelected = (rows: IdlObject[]) => (new Set(rows.map(row => row.current_resource().id())).size !== 1);
this.cancelNotAppropriate = (rows: IdlObject[]) => (this.noSelectedRows(rows) || ('pickedUp' === this.status));
+ this.pickupNotAppropriate = (rows: IdlObject[]) => (this.noSelectedRows(rows) || ('pickupReady' !== this.status));
this.returnNotAppropriate = (rows: IdlObject[]) => {
if (this.noSelectedRows(rows)) {
return true;
+ } else if (this.status && ('pickupReady' === this.status)) {
+ return true;
} else {
rows.forEach(row => {
if ((null == row.pickup_time()) || row.return_time()) { return true; }
this.reloadGrid = () => { this.grid.reload(); };
+ this.pickupSelected = (reservations: IdlObject[]) => {
+ const pickupOne = (thing: IdlObject) => {
+ if (!thing) { return; }
+ this.pickupResource(thing).subscribe(
+ () => pickupOne(reservations.shift()));
+ };
+ pickupOne(reservations.shift());
+ };
+
this.returnSelected = (reservations: IdlObject[]) => {
const returnOne = (thing: IdlObject) => {
if (!thing) { return; }
returnOne(reservations.shift());
};
+ this.pickupResource = (reservation: IdlObject) => {
+ return this.net.request(
+ 'open-ils.circ',
+ 'open-ils.circ.reservation.pickup',
+ this.auth.token(),
+ {'patron_barcode': reservation.usr().card().barcode(), 'reservation': reservation})
+ .pipe(tap(
+ (success) => {
+ this.onPickup.emit(reservation);
+ this.grid.reload(); },
+ (error) => { console.debug(error); }
+ ));
+ };
+
this.returnResource = (reservation: IdlObject) => {
return this.net.request(
'open-ils.circ',
(error) => { console.debug(error); }
));
};
+
this.listReadOnlyFields = () => {
- let list = "usr,xact_start,request_time,capture_time,pickup_time,return_time,capture_staff,target_resource_type,current_resource,target_resource,unrecovered,request_library,pickup_library,fine_interval,fine_amount,max_fine";
- if (this.status) { list = list + ",start_time"; }
- if ('returnedToday' === this.status) { list = list + ",end_time"; }
+ let list = 'usr,xact_start,request_time,capture_time,pickup_time,return_time,capture_staff,target_resource_type,' +
+ 'current_resource,target_resource,unrecovered,request_library,pickup_library,fine_interval,fine_amount,max_fine';
+ if (this.status && ('pickupReady' !== this.status)) { list = list + ',start_time'; }
+ if (this.status && ('returnedToday' === this.status)) { list = list + ',end_time'; }
return list;
- }
+ };
+
+ this.startTimeShouldBeFuture = (fieldName: string, value: Moment, record: IdlObject) => {
+ if (Moment(value) < Moment()) {
+ return 'Start time must be in the future';
+ }
+ return '';
+ };
+
+ this.handleRowActivate = (row: IdlObject) => {
+ if (this.status) {
+ if ('returnReady' === this.status) {
+ this.returnResource(row).subscribe();
+ } else if ('pickupReady' === this.status) {
+ this.pickupResource(row).subscribe();
+ } else if ('returnedToday' === this.status) {
+ this.toast.warning('Cannot edit this reservation');
+ } else {
+ this.showEditDialog(row);
+ }
+ } else {
+ this.showEditDialog(row);
+ }
+ };
}
showEditDialog(idlThing: IdlObject) {
<eg-title i18n-prefix i18n-suffix prefix="Booking" suffix="Return"></eg-title>
<!-- TODO: DRY This out: there has to be a good way to reuse the template -->
-<ngb-tabset (tabChange)="resetEverything()">
- <ngb-tab title="By patron" i18n-title>
+<ngb-tabset (tabChange)="handleTabChange($event)" [activeId]="selectedTab">
+ <ngb-tab title="By patron" i18n-title id="patron">
<ng-template ngbTabContent>
<div class="row">
<div class="col-md-4">
</div>
<div *ngIf="patronId">
<h2 class="text-center" i18n>Ready for return</h2>
- <eg-reservations-grid [patron]="patronId" status="pickedUp" persistSuffix="return.patron.picked_up"></eg-reservations-grid>
+ <eg-reservations-grid #patronReady [patron]="patronId" status="returnReady" persistSuffix="return.patron.picked_up"></eg-reservations-grid>
<h2 class="text-center" i18n>Returned today</h2>
- <eg-reservations-grid [patron]="patronId" status="returnedToday" persistSuffix="return.patron.returned"></eg-reservations-grid>
+ <eg-reservations-grid #patronReturned [patron]="patronId" status="returnedToday" persistSuffix="return.patron.returned"></eg-reservations-grid>
</div>
</ng-template>
</ngb-tab>
- <ngb-tab title="By resource" i18n-title>
+ <ngb-tab title="By resource" i18n-title id="resource">
<ng-template ngbTabContent>
<div class="input-group flex-nowrap">
<div class="input-group-prepend">
</div>
<div *ngIf="patronId">
<h2 class="text-center" i18n>Ready for return</h2>
- <eg-reservations-grid [resource]="patronId" status="pickedUp" persistSuffix="return.patron.picked_up"></eg-reservations-grid>
+ <eg-reservations-grid #resourceReady [resource]="patronId" status="returnReady" persistSuffix="return.patron.picked_up"></eg-reservations-grid>
<h2 class="text-center" i18n>Returned today</h2>
- <eg-reservations-grid [patron]="patronId" status="returnedToday" persistSuffix="return.patron.returned"></eg-reservations-grid>
+ <eg-reservations-grid #resourceReturned [patron]="patronId" status="returnedToday" persistSuffix="return.patron.returned"></eg-reservations-grid>
</div>
</ng-template>
</ngb-tab>
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, Input, OnInit, ViewChild } from '@angular/core';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import { NgbTabChangeEvent } from '@ng-bootstrap/ng-bootstrap';
+import {Observable} from 'rxjs';
+import {single} from 'rxjs/operators';
import { GridDataSource } from '@eg/share/grid/grid';
import { Pager } from '@eg/share/util/pager';
import {PatronService} from '@eg/staff/share/patron.service';
import {AuthService} from '@eg/core/auth.service';
import {IdlObject} from '@eg/core/idl.service';
import {NetService} from '@eg/core/net.service';
-import {Observable} from 'rxjs';
-import {single} from 'rxjs/operators';
+import {ReservationsGridComponent} from './reservations-grid.component';
+import {ServerStoreService} from '@eg/core/server-store.service';
+import {ToastService} from '@eg/share/toast/toast.service';
@Component({
patronId: number;
retrievePatronByBarcode: () => void;
retrievePatronByResource: () => void;
+ selectedTab: 'patron' | 'resource' = 'patron';
noSelectedRows: (rows: IdlObject[]) => boolean;
- resetEverything: () => void;
+ handleTabChange: ($event: NgbTabChangeEvent) => void;
+ @ViewChild('patronReady') patronReady: ReservationsGridComponent;
+ @ViewChild('patronReturned') patronReturned: ReservationsGridComponent;
+ @ViewChild('resourceReady') resourceReady: ReservationsGridComponent;
+ @ViewChild('resourceReturned') resourceReturned: ReservationsGridComponent;
constructor(
private auth: AuthService,
private net: NetService,
private pcrud: PcrudService,
- private patron: PatronService
+ private patron: PatronService,
+ private route: ActivatedRoute,
+ private router: Router,
+ private store: ServerStoreService,
+ private toast: ToastService
) {
}
ngOnInit() {
+ this.route.paramMap.subscribe((params: ParamMap) => {
+ this.patronId = +params.get('patron_id');
+ if (this.patronId) {
+ this.pcrud.search('au', {
+ 'id': this.patronId,
+ }, {
+ limit: 1,
+ flesh: 1,
+ flesh_fields: {'au': ['card']}
+ }).subscribe(
+ (resp) => {
+ this.patronBarcode = resp.card().barcode();
+ this.patronReady.reloadGrid();
+ this.patronReturned.reloadGrid();
+ }, (err) => { console.debug(err); }
+ );
+ } else {
+ this.store.getItem('eg.booking.return.tab').then(tab => {
+ if (tab) { this.selectedTab = tab; }
+ });
+ }
+ });
+
this.retrievePatronByBarcode = () => {
if (this.patronBarcode) {
this.patron.bcSearch(this.patronBarcode).pipe(single()).subscribe(
- resp => { this.patronId = resp[0]['id']; }
+ resp => { this.router.navigate(['/staff', 'booking', 'return', 'by_patron', resp[0].id]); },
+ err => { this.toast.danger('No patron found with this barcode'); },
);
}
};
+
this.retrievePatronByResource = () => {
if (this.resourceBarcode) {
this.pcrud.search('brsrc', {'barcode': this.resourceBarcode}, {
flesh_fields: {'brsrc': ['curr_rsrcs']},
select: {'curr_rsrcs': {'return_time': null, 'pickup_time': {'!=': null}}}
}).subscribe((resp) => {
- if (resp.curr_rsrcs().usr()) {
- this.patronId = resp.curr_rsrcs().usr();
+ if (resp.curr_rsrcs()[0].usr()) {
+ this.patronId = resp.curr_rsrcs()[0].usr();
+ this.resourceReady.reloadGrid();
+ this.resourceReturned.reloadGrid();
}
});
}
};
this.noSelectedRows = (rows: IdlObject[]) => (rows.length === 0);
- this.resetEverything = () => {
+ this.handleTabChange = ($event) => {
+ this.store.setItem('eg.booking.return.tab', $event.nextId);
+ this.router.navigate(['/staff', 'booking', 'return']);
this.resourceBarcode = null;
this.patronBarcode = null;
this.patronId = null;
component: PullListComponent
}, {
path: 'return',
- component: ReturnComponent
- },
+ children: [
+ {path: '', component: ReturnComponent},
+ {path: 'by_patron/:patron_id', component: ReturnComponent},
+ ]},
];
@NgModule({
<span class="material-icons">trending_up</span>
<span i18n>Pick Up Reservations</span>
</a>
- <a class="dropdown-item" href="/eg/staff/booking/legacy/booking/return">
+ <a class="dropdown-item" href="staff/booking/return">
<span class="material-icons">trending_down</span>
<span i18n>Return Reservations</span>
</a>
'Sticky setting for filter tab in Manage Reservations',
'cwst', 'label')
), (
+ 'eg.booking.return.tab', 'gui', 'string',
+ oils_i18n_gettext(
+ 'booking.return.tab',
+ 'Sticky setting for tab in Booking Return',
+ 'cwst', 'label')
+), (
'eg.booking.pickup.ready.only_show_captured', 'gui', 'bool',
oils_i18n_gettext(
'booking.pickup.ready.only_show_captured',
'Sticky setting for filter tab in Manage Reservations',
'cwst', 'label')
), (
+ 'eg.booking.return.tab', 'gui', 'string',
+ oils_i18n_gettext(
+ 'booking.return.tab',
+ 'Sticky setting for tab in Booking Return',
+ 'cwst', 'label')
+), (
'eg.booking.pickup.ready.only_show_captured', 'gui', 'bool',
oils_i18n_gettext(
'booking.pickup.ready.only_show_captured',
</a>
</li>
<li>
- <a href="./booking/legacy/booking/return?patron_barcode={{patron().card().barcode()}}" target="_top">
+ <a href="/eg2/staff/booking/return/by_patron/{{patron().id()}}" target="_top">
[% l('Booking: Return Reservations') %]
</a>
</li>
</a>
</li>
<li>
- <a href="./booking/legacy/booking/return" target="_self">
+ <a href="/eg2/staff/booking/return" target="_self">
<span class="glyphicon glyphicon-import"></span>
[% l('Return Reservations') %]
</a>