]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.html
LP#2000270 Staff catalog lets staff pick invalid pickup locations
[Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / catalog / hold / hold.component.html
1
2 <eg-patron-search-dialog #patronSearch></eg-patron-search-dialog>
3 <eg-barcode-select #barcodeSelect></eg-barcode-select>
4 <eg-worklog-strings-components></eg-worklog-strings-components>
5
6 <eg-alert-dialog #activeDateAlert
7   i18n-dialogTitle i18n-dialogBody
8   dialogTitle="Invalid Hold Activation Date"
9   dialogBody="Hold activation date {{activeDateYmd}} is not valid.
10     Please chose a date in the future">
11 </eg-alert-dialog>
12
13 <div class="row">
14   <div class="col-lg-1">
15     <button type="button" class="btn btn-info label-with-material-icon"
16       (click)="goBack()" [disabled]="hasNoHistory()">
17       <span class="material-icons" aria-hidden="true">keyboard_backspace</span>
18       <span i18n>Return</span>
19     </button>
20   </div>
21   <div class="col-lg-5">
22     <ng-container *ngIf="badBarcode">
23       <div class="alert alert-danger" i18n>
24         Barcode '{{badBarcode}}' not found.
25       </div>
26     </ng-container>
27     <ng-container *ngIf="!badBarcode">
28       <h3 i18n>Place Hold
29         <small *ngIf="user">
30         ({{user.pref_family_name() ? user.pref_family_name() : user.family_name()}},
31         {{user.pref_first_given_name() ? user.pref_first_given_name() :user.first_given_name()}})
32         </small>
33       </h3>
34     </ng-container>
35   </div>
36   <div class="col-lg-2">
37     <button type="button" class="btn btn-outline-dark btn-sm" (click)="searchPatrons()">
38       <span class="material-icons mat-icon-in-button align-middle" aria-hidden="true">search</span>
39       <span class="align-middle" i18n>Search for Patron</span>
40     </button>
41   </div>
42 </div>
43
44 <!-- eslint-disable @angular-eslint/template/accessibility-interactive-supports-focus -->
45 <form class="form form-validated common-form"
46   autocomplete="off" (keydown.enter)="$event.preventDefault()">
47   <!-- eslint-enable @angular-eslint/template/accessibility-interactive-supports-focus -->
48   <div class="row">
49     <div class="col-lg-6 common-form striped-odd">
50       <div class="row mt-2">
51         <div class="col-lg-6">
52           <div class="form-check">
53             <input class="form-check-input" type="radio"
54               (change)="holdForChanged()"
55               id="hold-for-patron"
56               name="holdFor" value="patron" [(ngModel)]="holdFor"/>
57             <label class="form-label form-check-label" for="hold-for-patron" i18n>
58               Place hold for patron by barcode:
59             </label>
60           </div>
61         </div>
62         <div class="col-lg-6">
63           <div class="input-group">
64             <input type='text' class="form-control" name="userBarcode"
65               [disabled]="holdFor!=='patron'" id='patron-barcode'
66               aria-label="Patron barcode" i18n-aria-label
67               (ngModelChange)="debounceUserBarcodeLookup($event)"
68               (paste)="debounceUserBarcodeLookup($event)"
69               [(ngModel)]="userBarcode"/>
70             <div class="input-group-text">
71               <button type="button" class="btn btn-outline-dark" (click)="userBarcodeChanged()">Submit</button>
72             </div>
73           </div>
74         </div>
75       </div>
76       <div class="row mt-2">
77         <div class="col-lg-6">
78           <div class="form-check">
79             <input class="form-check-input" type="radio"
80               (change)="holdForChanged()"
81               id="hold-for-staff"
82               name="holdFor" value="staff" [(ngModel)]="holdFor"/>
83             <label class="form-label form-check-label" i18n for="hold-for-staff">
84               Place hold for this staff account:
85             </label>
86           </div>
87         </div>
88         <div class="col-lg-6 fw-bold">{{requestor.usrname()}}</div>
89       </div>
90       <div class="row mt-2">
91         <div class="col-lg-6">
92           <label class="form-label" for="pickupLibSelect" i18n>Pickup Location: </label>
93         </div>
94         <div class="col-lg-6">
95           <eg-org-select domId="pickupLibSelect" (onChange)="pickupLib = $event ? $event.id() : null"
96             [disableOrgs]="disableOrgs" [applyOrgId]="pickupLib" [required]="true"></eg-org-select>
97         </div>
98       </div>
99       <div class="row mt-2">
100         <div class="col-lg-6">
101           <div class="form-check">
102             <input class="form-check-input" type="checkbox" id="suspend"
103               name="suspend" [(ngModel)]="suspend"/>
104             <label class="form-label form-check-label" for="suspend" i18n>Suspend Hold</label>
105           </div>
106         </div>
107         <div class="col-lg-6">
108           <div [ngClass]="{'border border-danger rounded': activeDateInvalid}">
109             <eg-date-select [(ngModel)]="activeDate" name='active-date'
110               (onChangeAsYmd)="activeDateYmd = $event"
111               (onChangeAsDate)="setActiveDate($event)"
112               (onChangeAsIso)="activeDateSelected($event)" [disabled]="!suspend">
113             </eg-date-select>
114           </div>
115         </div>
116       </div>
117       <div class="row mt-2" *ngIf="multiHoldsActive">
118         <div class="col-lg-6">
119           <label class="form-label" for='multi-hold-count' i18n>Number of copies:</label>
120         </div>
121         <div class="col-lg-6">
122           <select class="form-select" name="multi-hold-count"
123             id="multi-hold-count" [(ngModel)]="multiHoldCount">
124             <option [value]="num"
125               *ngFor="let num of holdCountRange()">{{num}}</option>
126           </select>
127         </div>
128       </div>
129
130     </div><!-- left column -->
131     <div class="col-lg-6">
132       <div class="card">
133         <div class="card-header">
134           <h4 i18n>Notifications</h4>
135         </div>
136         <ul class="list-group list-group-flush">
137           <li class="list-group-item d-flex">
138             <div class="flex-1">
139               <div class="form-check">
140                 <input class="form-check-input" type="checkbox" name="notifyEmail"
141                   id="notifyEmail"
142                   [disabled]="!user || !user.email()" [(ngModel)]="notifyEmail"/>
143                 <label class="form-label form-check-label" for="notifyEmail" i18n>Notify by Email</label>
144               </div>
145             </div>
146             <div class="flex-1">
147               <div class="input-group">
148                 <label for="userEmail" class="form-label input-group-text" i18n>Email Address</label>
149                 <input type="text" class="form-control" name="userEmail"
150                   id="userEmail"
151                   [disabled]="true" value="{{user ? user.email() : ''}}"/>
152               </div>
153             </div>
154           </li>
155           <li class="list-group-item d-flex">
156             <div class="flex-1">
157               <div class="form-check">
158                 <input class="form-check-input" type="checkbox"
159                   id="notifyPhone"
160                   name="notifyPhone" [(ngModel)]="notifyPhone"/>
161                 <label class="form-label form-check-label" for="notifyPhone" i18n>Notify by Phone</label>
162               </div>
163             </div>
164             <div class="flex-1">
165               <div class="input-group">
166                 <label for="phoneValue"  class="form-label input-group-text" i18n>Phone Number</label>
167                 <input type="text" class="form-control" [disabled]="!notifyPhone"
168                   name="phoneValue" id="phoneValue" [(ngModel)]="phoneValue"/>
169               </div>
170             </div>
171           </li>
172           <li *ngIf="smsEnabled" class="list-group-item d-flex">
173             <div class="flex-1">
174               <div class="form-check">
175                 <input class="form-check-input" type="checkbox" id="notifySms"
176                   name="notifySms" [(ngModel)]="notifySms"/>
177                 <label class="form-label form-check-label" for="notifySms" i18n>Notify by SMS</label>
178               </div>
179             </div>
180             <div class="flex-1">
181               <div class="input-group">
182                 <label for="smsValue"  class="form-label input-group-text" i18n>SMS Number</label>
183                 <input type="text" class="form-control" [disabled]="!notifySms"
184                   id="smsValue" name="smsValue" [(ngModel)]="smsValue"
185                   [required]="notifySms"/>
186               </div>
187             </div>
188           </li>
189           <li *ngIf="smsEnabled" class="list-group-item d-flex">
190             <div class="flex-1">
191               <label class="form-label" for="smsCarriers" i18n>SMS Carrier</label>
192             </div>
193             <div class="flex-1">
194               <eg-combobox [disabled]="!notifySms" #smsCbox
195                 domId="smsCarriers" [required]="notifySms"
196                 placeholder="SMS Carriers" i18n-placeholder
197                 [entries]="smsCarriers">
198               </eg-combobox>
199             </div>
200           </li>
201           <li class="list-group-item">
202             <button type="button" class="btn btn-success" (click)="placeHolds()"
203               [disabled]="!readyToPlaceHolds()" i18n>Place Hold(s)</button>
204             <button type="button" class="btn btn-outline-dark ms-2" (click)="resetForm()" i18n>Reset</button>
205           </li>
206         </ul><!-- col -->
207       </div><!-- row -->
208     </div><!--card -->
209   </div><!-- col -->
210 </form>
211
212 <div class="row"><div class="col-lg-12"><hr/></div></div>
213
214 <div class="row pt-3 ms-1 me-1 d-flex">
215   <div class="">
216     <span class="fw-bold" i18n>Placing
217       <ng-container *ngIf="holdType === 'M'">METARECORD</ng-container>
218       <ng-container *ngIf="holdType === 'T'">TITLE</ng-container>
219       <ng-container *ngIf="holdType === 'V'">CALL NUMBER</ng-container>
220       <ng-container *ngIf="holdType === 'F'">FORCE ITEM</ng-container>
221       <ng-container *ngIf="holdType === 'C'">ITEM</ng-container>
222       <ng-container *ngIf="holdType === 'R'">RECALL</ng-container>
223       <ng-container *ngIf="holdType === 'I'">ISSUANCE</ng-container>
224       <ng-container *ngIf="holdType === 'P'">PARTS</ng-container>
225       hold on record(s)
226     </span>
227   </div>
228   <div class="flex-1"> </div>
229   <div>
230     <span class="ps-3" *ngIf="isItemHold()">
231       <span i18n>Item-Level Hold Options:</span>
232       <span class="ps-2">
233         <a routerLink="/staff/catalog/hold/C" queryParamsHandling="merge">
234           <button type="button" [disabled]="holdType === 'C'" class="btn btn-outline-primary"
235             i18n>Item Hold</button>
236         </a>
237       </span>
238       <span class="ps-2">
239         <a routerLink="/staff/catalog/hold/R" queryParamsHandling="merge">
240           <button type="button" [disabled]="holdType === 'R'" class="btn btn-outline-primary"
241             i18n>Recall Hold</button>
242         </a>
243       </span>
244       <span class="ps-2">
245         <a routerLink="/staff/catalog/hold/F" queryParamsHandling="merge">
246           <button type="button" [disabled]="holdType === 'F'" class="btn btn-outline-primary"
247             i18n>Force Item Hold</button>
248         </a>
249       </span>
250     </span>
251   </div>
252 </div>
253
254 <ng-template #anyValue>
255   <span class="fst-italic" i18n>ANY</span>
256 </ng-template>
257
258 <!--
259     TODO: add a section per hold context for metarecord holds
260     listing the possible formats and languages.
261
262     TODO: add a secion per hold context for T holds providing a
263     link to the metarecord hold equivalent (AKA "Advanced Hold
264     Options") for each record that has selectable filters (and
265     only when metarecord holds are enabled).
266 -->
267
268 <div class="hold-records-list common-form striped-even">
269
270   <div class="row mt-2 ms-1 me-1 fw-bold">
271     <div class="col-lg-1" i18n>Format</div>
272     <div class="col-lg-2" i18n>Title</div>
273     <div class="col-lg-1" i18n>Author</div>
274     <div class="col-lg-2" i18n>Part</div>
275     <div class="col-lg-2" i18n>Call Number</div>
276     <div class="col-lg-1" i18n>Barcode</div>
277     <div class="col-lg-2" i18n>Holds Status</div>
278     <div class="col-lg-1" i18n>Override</div>
279   </div>
280   <div class="row mt-1 ms-1 me-1" *ngIf="showOverrideAll()">
281     <div class="col-lg-12">
282       <div class="row">
283         <div class="col-lg-1 ms-auto">
284           <button type="button" class="btn btn-info" i18n
285             (click)="overrideAll()">
286             Override All
287           </button>
288         </div>
289       </div>
290     </div>
291   </div>
292   <div class="row mt-1 ms-1 me-1" *ngFor="let ctx of holdContexts">
293     <div class="col-lg-12" *ngIf="ctx.holdMeta">
294       <div class="row">
295         <div class="col-lg-1">
296           <ng-container
297             *ngFor="let code of ctx.holdMeta.bibSummary.attributes.icon_format">
298             <img class="pe-1"
299               alt=""
300               src="/images/format_icons/icon_format/{{code}}.png"/>
301           </ng-container>
302         </div>
303         <!-- TODO: link for a metarecord should
304             jump to constituent bib list search page? -->
305         <div class="col-lg-2">
306           <a routerLink="/staff/catalog/record/{{ctx.holdMeta.bibId}}">
307             {{ctx.holdMeta.bibSummary.display.title}}
308           </a>
309         </div>
310         <div class="col-lg-1">{{ctx.holdMeta.bibSummary.display.author}}</div>
311         <div class="col-lg-2">
312           <ng-container *ngIf="ctx.holdMeta.parts.length">
313             <select class="form-select"  (change)="setPart(ctx, $event)"
314               [ngModel]="ctx.holdMeta.part ? ctx.holdMeta.part.id() : (ctx.holdMeta.part_required ? ctx.holdMeta.parts[0].id() : '')">
315               <option *ngIf="!ctx.holdMeta.part_required" value="" i18n>Any Part</option>
316               <option *ngFor="let part of ctx.holdMeta.parts"
317                 value="{{part.id()}}">{{part.label()}}</option>
318             </select>
319           </ng-container>
320           <ng-container *ngIf="ctx.holdMeta.parts.length === 0">
321             <ng-container *ngIf="ctx.holdMeta.part">
322               <span>{{ctx.holdMeta.part.label()}}</span>
323             </ng-container>
324             <ng-container *ngIf="!ctx.holdMeta.part">
325               <span i18n>N/A</span>
326             </ng-container>
327           </ng-container>
328         </div>
329         <div class="col-lg-2">
330           <ng-container *ngIf="ctx.holdMeta.callNum; else anyValue">
331             {{ctx.holdMeta.callNum.label()}}
332           </ng-container>
333         </div>
334         <div class="col-lg-1">
335           <ng-container *ngIf="ctx.holdMeta.copy; else anyValue">
336             {{ctx.holdMeta.copy.barcode()}}
337           </ng-container>
338         </div>
339         <div class="col-lg-2">
340           <ng-container *ngIf="!ctx.lastRequest && !ctx.processing">
341             <div class="alert alert-info p-1 ms-2" i18n>Hold Pending</div>
342           </ng-container>
343           <ng-container *ngIf="ctx.processing">
344             <div class="alert alert-primary p-1 ms-2" i18n>Hold Processing...</div>
345           </ng-container>
346           <ng-container *ngIf="ctx.lastRequest">
347             <ng-container *ngIf="ctx.lastRequest.result.success">
348               <div class="alert alert-success p-1 ms-2" i18n>Hold Succeeded</div>
349             </ng-container>
350             <ng-container *ngIf="!ctx.lastRequest.result.success">
351               <div class="alert alert-danger p-1 ms-2"
352                 title="{{ctx.lastRequest.result.evt.textcode}}">
353                 {{ctx.lastRequest.result.evt.textcode}}
354               </div>
355             </ng-container>
356           </ng-container>
357         </div>
358         <div class="col-lg-1">
359           <ng-container *ngIf="canOverride(ctx)">
360             <button type="button" class="btn btn-info" (click)="override(ctx)">Override</button>
361           </ng-container>
362         </div>
363       </div>
364       <!-- note: using inline style since class-level styling for rows
365           is superseded by the striped-even styling of the container -->
366       <div class="row" *ngIf="hasMetaFilters(ctx)"
367         style="background-color:inherit; border:none">
368         <div class="col-lg-1"><label class="form-label" i18n>Formats: </label></div>
369         <div class="col-lg-11 d-flex">
370           <ng-container
371             *ngFor="let ccvm of ctx.holdMeta.metarecord_filters.formats">
372             <div class="form-check ms-3">
373               <label class="form-label form-check-label ms-1" for="hold-include-format-{{ccvm.code()}}">
374               <input class="form-check-input" type="checkbox" id="hold-include-format-{{ccvm.code()}}"
375                 [disabled]="ctx.holdMeta.metarecord_filters.formats.length === 1"
376                 [(ngModel)]="ctx.selectedFormats.formats[ccvm.code()]"/>
377               <img class="ms-1"
378                 alt=""
379                 src="/images/format_icons/icon_format/{{ccvm.code()}}.png"/>
380
381                 {{ccvm.search_label() || ccvm.value()}}
382               </label>
383             </div>
384           </ng-container>
385         </div>
386       </div>
387       <div class="row" *ngIf="hasMetaFilters(ctx)"
388         style="background-color:inherit; border:none">
389         <div class="col-lg-1"><label class="form-label" i18n>Languages: </label></div>
390         <div class="col-lg-11 d-flex">
391           <ng-container
392             *ngFor="let ccvm of ctx.holdMeta.metarecord_filters.langs">
393             <div class="form-check ms-3">
394               <label class="form-label form-check-label ms-1" for="hold-include-lang-{{ccvm.value()}}">
395               <input class="form-check-input" type="checkbox" id="hold-include-lang-{{ccvm.value()}}"
396                 [disabled]="ctx.holdMeta.metarecord_filters.langs.length === 1"
397                 [(ngModel)]="ctx.selectedFormats.langs[ccvm.code()]"/>
398
399                 {{ccvm.search_label() || ccvm.value()}}
400               </label>
401             </div>
402           </ng-container>
403         </div>
404       </div>
405     </div>
406   </div>
407 </div>
408
409