LP2000482 Angular 15 and Bootstrap 5 upgrade
[evergreen-equinox.git] / Open-ILS / src / eg2 / src / app / staff / catalog / search-form.component.html
1 <ng-template #ccvmOption let-list="list">
2   <ng-container *ngFor="let ccv of list">
3   <option *ngIf="ccv.is_simple() === 't'" value="{{ccv.code()}}">
4     {{ccv.search_label() || ccv.value()}}
5     <ng-container *ngIf="ccv.opac_visible() === 'f'" i18n>(Hidden)</ng-container>
6   </option>
7   </ng-container>
8   <ng-container *ngFor="let ccv of list">
9   <option *ngIf="ccv.is_simple() === 'f'" value="{{ccv.code()}}">
10     {{ccv.search_label() || ccv.value()}}
11     <ng-container *ngIf="ccv.opac_visible() === 'f'" i18n>(Hidden)</ng-container>
12   </option>
13   </ng-container>
14 </ng-template>
15
16 <div id='staffcat-search-form'>
17
18   <div *ngIf="canBeHidden()" class="row pt-1 pe-2">
19     <div class="col-lg-12 d-flex">
20       <div class="flex-1"></div><!-- push right -->
21       <a (click)="toggleFormDisplay()" class="label-with-material-icon no-href">
22         <ng-container *ngIf="hideForm()" i18n>
23           Show Search Form <span class="material-icons" aria-hidden="true">unfold_more</span>
24         </ng-container>
25         <ng-container *ngIf="!hideForm()" i18n>
26           Hide Search Form <span class="material-icons" aria-hidden="true">unfold_less</span>
27         </ng-container>
28       </a>
29     </div>
30   </div>
31   
32   <div *ngIf="!hideForm()" class="row pt-3 pb-1 mb-1">
33   <div class="col-lg-8">
34     <ul ngbNav #searchTabs="ngbNav" [(activeId)]="searchTab" (navChange)="onNavChange($event)" [keyboard]="true" [roles]="false" role="tablist" class="nav-tabs">
35       <li role="presentation" [ngbNavItem]="'term'">
36         <a i18n ngbNavLink role="tab">Keyword Search</a>
37         <ng-template ngbNavContent>
38           <div class="row row-cols-auto d-flex align-items-end mt-1"
39             *ngFor="let q of context.termSearch.query; let idx = index; trackBy:trackByIdx">
40             <div class="col-lg-2 pe-1">
41               <div *ngIf="idx === 0">
42                 <label i18n class="form-label select-label" for="context-termSearch-format">Format</label>
43                 <select class="form-select" id="context-termSearch-format" [(ngModel)]="context.termSearch.format">
44                   <option i18n value=''>All Formats</option>
45                   <ng-container
46                     *ngTemplateOutlet="ccvmOption;context:{list:ccvmMap.search_format}">
47                   </ng-container> 
48                 </select>
49               </div>
50               <div *ngIf="idx > 0">
51                 <label i18n class="form-label select-label" for="context-termSearch-joinOp-{{idx}}">Join with</label>
52                 <select class="form-select" id="context-termSearch-joinOp-{{idx}}"
53                   [(ngModel)]="context.termSearch.joinOp[idx]">
54                   <option i18n value='&&'>And</option>
55                   <option i18n value='||'>Or</option>
56                 </select>
57               </div>
58             </div>
59             <div class="col-lg-2 ps-0 pe-2">
60               <label i18n class="form-label select-label" for="context-termSearch-fieldClass-{{idx}}">Catalog Field</label>
61               <select class="form-select" id="context-termSearch-fieldClass-{{idx}}"
62                 (change)="preventBogusCombos(idx)"
63                 [(ngModel)]="context.termSearch.fieldClass[idx]">
64                 <option i18n value='keyword'>Keyword</option>
65                 <option i18n value='title'>Title</option>
66                 <option i18n value='jtitle'>Journal Title</option>
67                 <option i18n value='author'>Author</option>
68                 <option i18n value='subject'>Subject</option>
69                 <option i18n value='series'>Series</option>
70                 <option i18n value='bookplate'
71                   *ngIf="showBookplate()">Digital Bookplate</option>
72               </select>
73             </div>
74             <div class="col-lg-2 ps-0 pe-2">
75               <label i18n class="form-label select-label" for="context-termSearch-matchOp-{{idx}}">Matching</label>
76               <select class="form-select" id="context-termSearch-matchOp-{{idx}}"
77                 [(ngModel)]="context.termSearch.matchOp[idx]">
78                 <option i18n value='contains'>Contains</option>
79                 <option i18n value='nocontains'>Does not contain</option>
80                 <option i18n value='phrase'>Contains phrase</option>
81                 <option [disabled]="context.termSearch.fieldClass[idx]==='keyword'"
82                   i18n value='exact'>Matches exactly</option>
83                 <option [disabled]="context.termSearch.fieldClass[idx]==='keyword'"
84                   i18n value='starts'>Starts with</option>
85               </select>
86             </div>
87             <div class="col-lg-4 ps-0 pe-2">
88               <div class="form-group">
89                 <div *ngIf="idx === 0">
90                   <label i18n class="form-label input-label" for='first-query-input'>Search Terms</label>
91                   <input type="text" class="form-control"
92                     id='first-query-input'
93                     [(ngModel)]="context.termSearch.query[idx]"
94                     (keyup.enter)="searchByForm()"
95                     placeholder="Query..."/>
96                 </div>
97                 <div *ngIf="idx > 0">
98                   <label i18n class="form-label input-label" for='context-termSearch-query-{{idx}}'>Search Terms</label>
99                   <input type="text" class="form-control"
100                     [(ngModel)]="context.termSearch.query[idx]"
101                     (keyup.enter)="searchByForm()"
102                     placeholder="Query..."/>
103                 </div>
104               </div>
105             </div>
106             <div class="col-lg-2 ps-0 pe-1">
107               <button class="btn btn-sm material-icon-button"
108                 (click)="addSearchRow(idx + 1)"
109                 i18n-title title="Add Search Row">
110                 <span class="material-icons">add_circle_outline</span>
111               </button>
112               <button class="btn btn-sm material-icon-button"
113                 [disabled]="context.termSearch.query.length < 2"
114                 (click)="delSearchRow(idx)"
115                 i18n-title title="Remove Search Row">
116                 <span class="material-icons">remove_circle_outline</span>
117               </button>
118               <button *ngIf="idx === 0"
119                 class="btn btn-sm material-icon-button" 
120                 type="button" (click)="toggleFilters()" 
121                 title="Toggle Search Filters" i18n-title>
122                 <span class="material-icons">more_vert</span>
123               </button>
124             </div>
125           </div>
126           <div class="mt-3 gx-3 row row-cols-auto">
127             <div class="col row-cols-auto d-flex align-items-end form-inline">
128               <div class="col form-inline-group">
129                 <label i18n class="form-label select-label" for="context-sort">Sort Results</label>
130                 <select class="form-select me-2" id="context-sort" [(ngModel)]="context.sort">
131                   <option value='' i18n>Sort by Relevance</option>
132                   <optgroup label="Sort by Title" i18n-label>
133                     <option value='titlesort' i18n>Title: A to Z</option>
134                     <option value='titlesort.descending' i18n>Title: Z to A</option>
135                   </optgroup>
136                   <optgroup label="Sort by Author" i18n-label>
137                     <option value='authorsort' i18n>Author: A to Z</option>
138                     <option value='authorsort.descending' i18n>Author: Z to A</option>
139                   </optgroup>
140                   <optgroup label="Sort by Publication Date" i18n-label>
141                     <option value='pubdate.descending' i18n>Date: Newest to Oldest</option>
142                     <option value='pubdate' i18n>Date: Oldest to Newest</option>
143                   </optgroup>
144                   <optgroup label="Sort by Popularity" i18n-label>
145                     <option value='popularity' i18n>Most Popular</option>
146                     <option value='poprel' i18n>Popularity Adjusted Relevance</option>
147                   </optgroup>
148                 </select>
149               </div>
150                 <div class="col checkbox ps-2 ms-2 pt-2">
151                   <label class="form-label" for="context-termSearch-available">
152                     <input type="checkbox" [(ngModel)]="context.termSearch.available" id="context-termSearch-available" />
153                     <span class="ps-1" i18n>Limit to Available</span>
154                   </label>
155                 </div>
156                 <div class="col checkbox ps-3 pt-2">
157                   <label class="form-label" for="context-termSearch-groupByMetarecord">
158                     <input type="checkbox" id="context-termSearch-groupByMetarecord"
159                       [(ngModel)]="context.termSearch.groupByMetarecord"/>
160                     <span class="ps-1" i18n>Group Formats/Editions</span>
161                   </label>
162                 </div>
163                 <div class="col checkbox ps-3 pt-2">
164                   <label class="form-label" for="context-global">
165                     <input type="checkbox" id="context-global" [(ngModel)]="context.global"/>
166                     <span class="ps-1" i18n>Results from All Libraries</span>
167                   </label>
168                 </div>
169                 <div class="col checkbox ps-3 pt-2" *ngIf="showExcludeElectronic()">
170                   <label class="form-label" for="context-termSearch-excludeElectronic">
171                     <input type="checkbox" id="context-termSearch-excludeElectronic"
172                       [(ngModel)]="context.termSearch.excludeElectronic"/>
173                     <span class="ps-1" i18n>Exclude Electronic Resources</span>
174                   </label>
175                 </div>
176               </div>
177           </div>
178           <div class="row" *ngIf="showFilters()">
179             <div class="col mt-3" *ngIf="searchFilters().includes('item_type')">
180               <label i18n class="form-label select-label" for="context-termSearch-ccvmFilters-item_type">Item Type</label>
181               <select class="form-select"  multiple="true" id="context-termSearch-ccvmFilters-item_type"
182                 [(ngModel)]="context.termSearch.ccvmFilters.item_type">
183                 <option value='' i18n>All Item Types</option>
184                 <ng-container
185                   *ngTemplateOutlet="ccvmOption;context:{list:ccvmMap.item_type}">
186                 </ng-container> 
187               </select>
188             </div>
189             <div class="col mt-3" *ngIf="searchFilters().includes('item_form')">
190               <label i18n class="form-label select-label" for="context-termSearch-ccvmFilters-item_form">Form</label>
191               <select class="form-select" multiple="true" id="context-termSearch-ccvmFilters-item_form"
192                 [(ngModel)]="context.termSearch.ccvmFilters.item_form">
193                 <option value='' i18n>All Item Forms</option>
194                 <ng-container
195                   *ngTemplateOutlet="ccvmOption;context:{list:ccvmMap.item_form}">
196                 </ng-container> 
197               </select>
198             </div>
199             <div class="col mt-3" *ngIf="searchFilters().includes('item_lang')">
200               <label i18n class="form-label select-label" for="context-termSearch-ccvmFilters-item_lang">Language</label>
201               <select class="form-select" id="context-termSearch-ccvmFilters-item_lang"
202                 [(ngModel)]="context.termSearch.ccvmFilters.item_lang" multiple="true">
203                 <option value='' i18n>All Languages</option>
204                 <ng-container
205                   *ngTemplateOutlet="ccvmOption;context:{list:ccvmMap.item_lang}">
206                 </ng-container> 
207               </select>
208             </div>
209             <div class="col mt-3" *ngIf="searchFilters().includes('audience')">
210               <label i18n class="form-label select-label" for="context-termSearch-ccvmFilters-audience">Audience</label>
211               <select class="form-select" id="context-termSearch-ccvmFilters-audience"
212                 [(ngModel)]="context.termSearch.ccvmFilters.audience" multiple="true">
213                 <option value='' i18n>All Audiences</option>
214                 <ng-container
215                   *ngTemplateOutlet="ccvmOption;context:{list:ccvmMap.audience}">
216                 </ng-container> 
217               </select>
218             </div>
219             <div class="col mt-3" *ngIf="searchFilters().includes('vr_format')">
220               <label i18n class="form-label select-label" for="context-termSearch-ccvmFilters-vr_format">Video Format</label>
221               <select class="form-select" id="context-termSearch-ccvmFilters-vr_format"
222                 [(ngModel)]="context.termSearch.ccvmFilters.vr_format" multiple="true">
223                 <option value='' i18n>All Video Formats</option>
224                 <ng-container
225                   *ngTemplateOutlet="ccvmOption;context:{list:ccvmMap.vr_format}">
226                 </ng-container> 
227               </select>
228             </div>
229             <div class="col mt-3" *ngIf="searchFilters().includes('bib_level')">
230               <label i18n class="form-label select-label" for="context-termSearch-ccvmFilters-bib_level">Bibliographic Level</label>
231               <select class="form-select" id="context-termSearch-ccvmFilters-bib_level"
232                 [(ngModel)]="context.termSearch.ccvmFilters.bib_level" multiple="true">
233                 <option value='' i18n>All Bib Levels</option>
234                 <ng-container
235                   *ngTemplateOutlet="ccvmOption;context:{list:ccvmMap.bib_level}">
236                 </ng-container> 
237               </select>
238             </div>
239             <div class="col mt-3" *ngIf="searchFilters().includes('lit_form')">
240               <label i18n class="form-label select-label" for="context-termSearch-ccvmFilters-lit_form">Literary Form</label>
241               <select class="form-select" id="context-termSearch-ccvmFilters-lit_form"
242                 [(ngModel)]="context.termSearch.ccvmFilters.lit_form" multiple="true">
243                 <option value='' i18n>All Literary Forms</option>
244                 <ng-container
245                   *ngTemplateOutlet="ccvmOption;context:{list:ccvmMap.lit_form}">
246                 </ng-container> 
247               </select>
248             </div>
249             <div class="col mt-3">
250               <label i18n class="form-label select-label" for="context-termSearch-copyLocations">Shelving Location</label>
251               <select class="form-select" id="context-termSearch-copyLocations"
252                 [(ngModel)]="context.termSearch.copyLocations" multiple="true">
253                 <option value='' i18n>All Shelving Locations</option>
254                 <option *ngFor="let loc of copyLocations" value="{{loc.id()}}" i18n>
255                   {{loc.name()}} ({{orgName(loc.owning_lib())}})
256                 </option>
257               </select>
258             </div>
259           </div>
260           <div class="row row-cols-auto mt-3" *ngIf="showFilters()">
261             <div class="col">
262               <div class="form-inline">
263                 <label class="form-label" for="pub-date1-input" i18n>Publication Year is</label>
264                 <label class="visually-hidden" for="pub-date-operator-select" i18n>compare publication dates using...</label>
265                 <select id="pub-date-operator-select" class="form-select ms-2" [(ngModel)]="context.termSearch.dateOp">
266                   <option value='is' i18n>Is</option>
267                   <option value='before' i18n>Before</option>
268                   <option value='after' i18n>After</option>
269                   <option value='between' i18n>Between</option>
270                 </select>
271                 <label i18n class="visually-hidden" for="context-termSearch-date1">Year, or beginning of year range</label>
272                 <input class="form-control ms-2" type="number" id="context-termSearch-date1"
273                   [(ngModel)]="context.termSearch.date1"/>
274                 <label i18n class="visually-hidden" for="context-termSearch-date2">End of year range</label>
275                   <input class="form-control ms-2" type="number"
276                   *ngIf="context.termSearch.dateOp === 'between'"
277                   [(ngModel)]="context.termSearch.date2"/>
278               </div>
279             </div>
280           </div>
281         </ng-template>
282       </li>
283       <li role="presentation" [ngbNavItem]="'ident'">
284         <a i18n ngbNavLink role="tab">Numeric Search</a>
285         <ng-template ngbNavContent>
286           <div class="row mt-4">
287             <div class="col-lg-12">
288               <div class="form-inline">
289                 <label for="ident-type" i18n>Query Type</label>
290                 <select class="form-select ms-2" name="ident-type"
291                   [(ngModel)]="context.identSearch.queryType">
292                   <option i18n value="identifier|isbn">ISBN</option>
293                   <option i18n value="identifier|upc">UPC</option>
294                   <option i18n value="identifier|issn">ISSN</option>
295                   <option i18n value="identifier|lccn">LCCN</option>
296                   <option i18n value="identifier|tcn">TCN</option>
297                   <option i18n value="item_barcode">Item Barcode</option>
298                 </select>
299                 <label for="ident-value" class="ms-2" i18n>Value</label>
300                 <input name="ident-value" id='ident-query-input' 
301                   type="text" class="form-control ms-2"
302                   [(ngModel)]="context.identSearch.value"
303                   (keyup.enter)="searchByForm()"
304                   placeholder="Numeric Query..."/>
305               </div>
306             </div>
307           </div>
308           <div class="row mt-2 gx-3 row-cols-auto justify-content-start align-items-center">
309             <label class="form-label col" for="ident-type" i18n>Query Type</label>
310             <div class="col">
311               <label class="form-label col-form-label" for="ident-type" i18n>Query Type</label>
312               <select class="form-select" name="ident-type"
313                 [(ngModel)]="context.identSearch.queryType">
314                 <option i18n value="identifier|isbn">ISBN</option>
315                 <option i18n value="identifier|upc">UPC</option>
316                 <option i18n value="identifier|issn">ISSN</option>
317                 <option i18n value="identifier|lccn">LCCN</option>
318                 <option i18n value="identifier|tcn">TCN</option>
319                 <option i18n value="item_barcode">Item Barcode</option>
320               </select>
321             </div>
322             <div class="col">
323               <label for="ident-value" class="form-label col-form-label" i18n>Value</label>
324             
325               <input name="ident-value" id='ident-query-input' 
326                 type="text" class="form-control"
327                 [(ngModel)]="context.identSearch.value"
328                 (keyup.enter)="searchByForm()"
329                 placeholder="Numeric Query..."/>
330             </div>
331           </div>
332         </ng-template>
333       </li>
334       <li role="presentation" [ngbNavItem]="'marc'">
335         <a i18n ngbNavLink role="tab">MARC Search</a>
336         <ng-template ngbNavContent>
337           <div class="mt-1">
338               <div class="row mt-2 gx-3 row-cols-auto" 
339                 *ngFor="let q of context.marcSearch.values; let idx = index; trackBy:trackByIdx">
340                 <label class="form-label col-form-label" for="marc-tag-{{idx}}" i18n>Tag</label>
341                 <div class="col ps-0">
342                   <input class="form-control ms-0" size="3" type="text" 
343                     name="marc-tag-{{idx}}" id="{{ idx === 0 ? 'first-marc-tag' : '' }}"
344                     [(ngModel)]="context.marcSearch.tags[idx]"
345                     (keyup.enter)="searchByForm()"/>
346                 </div>
347                 <label class="form-label col-form-label" for="marc-subfield-{{idx}}" i18n>Subfield</label>
348                 <div class="col ps-0">
349                   <input class="form-control ms-0" size="1" type="text" 
350                     name="marc-subfield-{{idx}}"
351                     [(ngModel)]="context.marcSearch.subfields[idx]"
352                     (keyup.enter)="searchByForm()"/>
353                 </div>
354                 <label class="form-label col-form-label"  for="marc-value-{{idx}}" i18n>Value</label>
355                 <div class="col ps-0">
356                   <input class="form-control ms-0" type="text" name="marc-value-{{idx}}"
357                     [(ngModel)]="context.marcSearch.values[idx]" 
358                     (keyup.enter)="searchByForm()"/>
359                 </div>
360                 <div class="col">
361                   <button class="btn btn-sm material-icon-button ms-2"
362                     (click)="addMarcSearchRow(idx + 1)">
363                     <span class="material-icons">add_circle_outline</span>
364                   </button>
365                 </div>
366                 <div class="col">
367                   <button class="btn btn-sm material-icon-button ps-0 ms-0"
368                     [disabled]="context.marcSearch.values.length < 2"
369                     (click)="delMarcSearchRow(idx)">
370                     <span class="material-icons">remove_circle_outline</span>
371                   </button>
372                 </div>
373               </div>
374           </div>
375           <div class="row mt-2">
376             <div class="checkbox ps-3">
377               <label>
378                 <input type="checkbox" [(ngModel)]="context.global"/>
379                 <span class="ps-1" i18n>Results from All Libraries</span>
380               </label>
381             </div>
382           </div>
383         </ng-template>
384       </li>
385       <li role="presentation" [ngbNavItem]="'browse'">
386         <a i18n ngbNavLink role="tab">Browse</a>
387         <ng-template ngbNavContent>
388           <div class="row gx-3 row-cols-auto">
389             <label class="form-label col-form-label" for="field-class" i18n>Browse for</label>
390             <div class="col ps-0">
391               <select class="form-select ms-0" name="field-class"
392                 [(ngModel)]="context.browseSearch.fieldClass">
393                 <option i18n value='title'>Title</option>
394                 <option i18n value='author'>Author</option>
395                 <option i18n value='subject'>Subject</option>
396                 <option i18n value='series'>Series</option>
397               </select>
398             </div>
399             <label class="form-label col-form-label" for="query"> starting with </label>
400             <div class="col ps-0">
401               <input type="text" class="form-control" 
402                 id='browse-term-input' name="query"
403                 [(ngModel)]="context.browseSearch.value"
404                 (keyup.enter)="searchByForm()"
405                 placeholder="Browse for..."/>
406             </div>
407           </div>
408         </ng-template>
409       </li>
410       <li role="presentation" [ngbNavItem]="'cnbrowse'">
411         <a i18n ngbNavLink role="tab">Shelf Browse</a>
412         <ng-template ngbNavContent>
413           <div class="row gx-3 row-cols-auto">
414             <label class="form-label col-form-label" for="cnbrowse-term-input" i18n>
415               Browse Call Numbers starting with 
416             </label>
417             <div class="col ps-0">
418               <input type="text" class="form-control ms-2" 
419                 id='cnbrowse-term-input' name="query"
420                 [(ngModel)]="context.cnBrowseSearch.value"
421                 (keyup.enter)="searchByForm()"
422                 placeholder="Browse Call Numbers..."/>
423             </div>
424           </div>
425         </ng-template>
426       </li>      
427     </ul>
428
429     <div [ngbNavOutlet]="searchTabs" class="mt-2"></div>
430
431   </div>
432   <div class="col-lg-4">
433     <div class="row">
434       <div class="col-lg-12">
435         <div class="card">
436           <div class="card-body">
437             <div class="float-end d-flex">
438               <eg-org-select 
439                 (onChange)="orgOnChange($event)"
440                 [initialOrg]="context.searchOrg"
441                 [placeholder]="'Library'" >
442               </eg-org-select>
443               <button class="btn btn-success me-1 ms-1" type="button"
444                 [disabled]="searchIsActive()"
445                 (click)="context.pager.offset=0;searchByForm()" i18n>
446                 Search
447               </button>
448               <button class="btn btn-warning me-1" type="button"
449                 [disabled]="searchIsActive()"
450                 (click)="context.reset()" i18n>
451                 Reset
452               </button>
453             </div>
454           </div>
455         </div>
456       </div>
457     </div>
458     <div class="row mt-1">
459       <div class="col-lg-12">
460         <eg-catalog-search-templates [searchTab]="searchTab">
461         </eg-catalog-search-templates>
462       </div>
463     </div>
464     <div class="row mt-1">
465       <div class="col-lg-12">
466         <eg-catalog-basket-actions></eg-catalog-basket-actions>
467       </div>
468     </div>
469   </div>
470   </div>
471   <div class="row mb-3 pb-3">
472     <ng-container *ngIf="context.result.global_summary as gs">
473       <ng-container *ngIf="gs.suggestions as sugg">
474         <div *ngIf="sugg.one_class_one_term as ocot" class="col-lg-12 d-flex align-content-center">
475           <span class="fw-bold fst-italic me-1" i18n>Did you mean:</span>
476           <span *ngFor="let s of ocot.suggestions; first as isFirst">
477             <span *ngIf="!isFirst" i18n>&nbsp; or &nbsp;</span>
478             <a routerLink="/staff/catalog/search" queryParamsHandling="merge"
479              [queryParams]="{query : s.suggestion, fieldClass : ocot.class}">
480                {{s.suggestion}}
481             </a>
482           </span>
483         </div>
484       </ng-container>
485     </ng-container>
486   </div>
487 </div>
488