return str;
}
+
+ // A search context can collect enough data for multiple search
+ // types to be searchable (e.g. users navigate through parts of a
+ // search form). Calling this method and providing a search type
+ // ensures the context is cleared of any data unrelated to the
+ // desired type.
+ scrub(searchType: string): void {
+
+ switch (searchType) {
+
+ case 'term': // AKA keyword search
+ this.marcSearch.reset();
+ this.browseSearch.reset();
+ this.identSearch.reset();
+ this.cnBrowseSearch.reset();
+ this.termSearch.hasBrowseEntry = '';
+ this.termSearch.browseEntry = null;
+ this.termSearch.fromMetarecord = null;
+ this.termSearch.facetFilters = [];
+ break;
+
+ case 'ident':
+ this.marcSearch.reset();
+ this.browseSearch.reset();
+ this.termSearch.reset();
+ this.cnBrowseSearch.reset();
+ break;
+
+ case 'marc':
+ this.browseSearch.reset();
+ this.termSearch.reset();
+ this.identSearch.reset();
+ this.cnBrowseSearch.reset();
+ break;
+
+ case 'browse':
+ this.marcSearch.reset();
+ this.termSearch.reset();
+ this.identSearch.reset();
+ this.cnBrowseSearch.reset();
+ this.browseSearch.pivot = null;
+ break;
+
+ case 'cnbrowse':
+ this.marcSearch.reset();
+ this.termSearch.reset();
+ this.identSearch.reset();
+ this.browseSearch.reset();
+ this.cnBrowseSearch.offset = 0;
+ break;
+ }
+ }
}
import {ConjoinedComponent} from './record/conjoined.component';
import {CnBrowseComponent} from './cnbrowse.component';
import {CnBrowseResultsComponent} from './cnbrowse/results.component';
+import {SearchFiltersComponent} from './search-filters.component';
@NgModule({
declarations: [
BrowseResultsComponent,
ConjoinedComponent,
HoldingsMaintenanceComponent,
+ SearchFiltersComponent,
CnBrowseComponent,
CnBrowseResultsComponent
],
'cat.marcedit.stack_subfields',
'cat.marcedit.flateditor',
'cat.holdings_show_copies',
- 'cat.holdings_show_vols'
+ 'cat.holdings_show_vols',
+ 'opac.staff_saved_search.size'
]).then(settings => {
this.staffCat.defaultSearchOrg =
this.org.get(settings['eg.search.search_lib']);
--- /dev/null
+
+<div class="d-flex justify-content-end">
+
+ <ng-container *ngIf="recentSearchesCount > 0">
+ <div ngbDropdown>
+ <button class="btn btn-light" id="recentSearches"
+ ngbDropdownToggle i18n>Recent Searches</button>
+ <div ngbDropdownMenu aria-labelledby="recentSearches">
+ <a class="dropdown-item" routerLink="" i18n>
+ test
+ </a>
+ </div>
+ </div>
+ </ng-container>
+
+ <div ngbDropdown>
+ <button class="btn btn-light" id="searchFilters"
+ ngbDropdownToggle i18n>Search Filters</button>
+ <div ngbDropdownMenu aria-labelledby="searchFilters">
+ <a class="dropdown-item" i18n (click)="open()">
+ Save Search
+ </a>
+ <!-- TODO show template name in confirm dialog -->
+ <a class="dropdown-item" i18n>
+ Delete Selected
+ </a>
+ <div class="dropdown-divider"></div>
+ <!-- TODO HIGHLIGHT SELECTED TEMPLATE -->
+ <a *ngFor="let tmpl of templates"
+ class="dropdown-item"
+ [ngClass]="{'font-weight-bold': tmpl.name === selectedTemplate}"
+ routerLink="/staff/catalog/search"
+ [queryParams]="tmpl.params">{{tmpl.name}}</a>
+ </div>
+ </div>
+</div>
+
+<ng-template #dialogContent>
+ <div class="modal-header bg-info">
+ <h4 class="modal-title" i18n>Save Template</h4>
+ <button type="button" class="close"
+ i18n-aria-label aria-label="Close" (click)="close()">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ <div class="row">
+ <div class="col-lg-4" i18n id="templateNameLabel">Template Name:</div>
+ <div class="col-lg-6">
+ <input class="form-control" [(ngModel)]="templateName"
+ aria-labelledby="templateNameLabel"/>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-success"
+ (click)="saveTemplate()" i18n>Save</button>
+ <button type="button" class="btn btn-warning"
+ (click)="close()" i18n>Cancel</button>
+ </div>
+</ng-template>
+
--- /dev/null
+import {Component, OnInit, Input} from '@angular/core';
+import {OrgService} from '@eg/core/org.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {CatalogService} from '@eg/share/catalog/catalog.service';
+import {CatalogUrlService} from '@eg/share/catalog/catalog-url.service';
+import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context';
+import {StaffCatalogService} from './catalog.service';
+import {AnonCacheService} from '@eg/share/util/anon-cache.service';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+
+const SAVED_TEMPLATES_SETTING = 'eg.catalog.search_templates';
+
+class SearchTemplate {
+ name: string;
+ params: any; // routerLink-compatible URL params object
+ constructor(name: string, params: any) {
+ this.name = name;
+ this.params = params;
+ }
+}
+
+@Component({
+ selector: 'eg-catalog-search-filters',
+ templateUrl: 'search-filters.component.html'
+})
+export class SearchFiltersComponent extends DialogComponent implements OnInit {
+
+ recentSearchesCount = 0;
+ context: CatalogSearchContext;
+ templates: SearchTemplate[] = [];
+ templateName: string;
+ selectedTemplate: string;
+
+ @Input() searchTab: string;
+
+ constructor(
+ private org: OrgService,
+ private store: ServerStoreService, // search templates
+ private cache: AnonCacheService, // recent searches
+ private cat: CatalogService,
+ private catUrl: CatalogUrlService,
+ private staffCat: StaffCatalogService,
+ private modal: NgbModal) {
+ super(modal);
+ }
+
+ ngOnInit() {
+ this.context = this.staffCat.searchContext;
+ this.org.settings('opac.staff_saved_search.size').then(sets => {
+ const size = sets['opac.staff_saved_search.size'];
+ if (size > 0) { this.recentSearchesCount = Number(size); }
+ });
+
+ this.getTemplates();
+ }
+
+ getTemplates(): Promise<any> {
+ this.templates = [];
+
+ return this.store.getItem(SAVED_TEMPLATES_SETTING).then(
+ templates => {
+ if (templates && templates.length) {
+ this.templates = templates;
+
+ // route index required to force the route to take
+ // effect. See ./catalog.service.ts
+ this.templates.forEach(tmpl =>
+ tmpl.params.ridx = this.staffCat.routeIndex++);
+ }
+ }
+ );
+ }
+
+ saveTemplate(): Promise<any> {
+ if (!this.templateName) { return; }
+
+ this.close();
+ const tmpContext = this.staffCat.cloneContext(this.context);
+ tmpContext.scrub(this.searchTab);
+ const urlParams = this.catUrl.toUrlParams(tmpContext);
+
+ // Some data should not go into the template.
+ delete urlParams.org;
+ delete urlParams.ridx;
+
+ // Clear the query values, but leave the query param in
+ // place since the CatalogUrl services expect to see one.
+ urlParams.query = [''];
+
+ this.templates.push(new SearchTemplate(this.templateName, urlParams));
+
+ return this.store.setItem(SAVED_TEMPLATES_SETTING, this.templates)
+ .then(_ => this.close());
+ }
+}
+
+
+
<eg-catalog-basket-actions></eg-catalog-basket-actions>
</div>
</div>
+ <div class="row mt-2">
+ <div class="col-lg-12">
+ <div class="float-right">
+ <eg-catalog-search-filters [searchTab]="searchTab">
+ </eg-catalog-search-filters>
+ </div>
+ </div>
+ </div>
</div>
</div>
// Form search overrides basket display
this.context.showBasket = false;
- switch (this.searchTab) {
+ this.context.scrub(this.searchTab);
- case 'term': // AKA keyword search
- this.context.marcSearch.reset();
- this.context.browseSearch.reset();
- this.context.identSearch.reset();
- this.context.cnBrowseSearch.reset();
- this.context.termSearch.hasBrowseEntry = '';
- this.context.termSearch.browseEntry = null;
- this.context.termSearch.fromMetarecord = null;
- this.context.termSearch.facetFilters = [];
- this.staffCat.search();
- break;
+ switch (this.searchTab) {
+ case 'term':
case 'ident':
- this.context.marcSearch.reset();
- this.context.browseSearch.reset();
- this.context.termSearch.reset();
- this.context.cnBrowseSearch.reset();
- this.staffCat.search();
- break;
-
case 'marc':
- this.context.browseSearch.reset();
- this.context.termSearch.reset();
- this.context.identSearch.reset();
- this.context.cnBrowseSearch.reset();
this.staffCat.search();
break;
case 'browse':
- this.context.marcSearch.reset();
- this.context.termSearch.reset();
- this.context.identSearch.reset();
- this.context.cnBrowseSearch.reset();
- this.context.browseSearch.pivot = null;
this.staffCat.browse();
break;
case 'cnbrowse':
- this.context.marcSearch.reset();
- this.context.termSearch.reset();
- this.context.identSearch.reset();
- this.context.browseSearch.reset();
- this.context.cnBrowseSearch.offset = 0;
this.staffCat.cnBrowse();
break;
}
--- /dev/null
+
+BEGIN;
+
+--SELECT evergreen.upgrade_deps_block_check('TODO', :eg_version);
+
+INSERT INTO config.workstation_setting_type (name, grp, datatype, label)
+VALUES (
+ -- hmm, we probably need a 'catalog' settings group
+ 'eg.catalog.search_templates', 'gui', 'object',
+ oils_i18n_gettext(
+ 'eg.catalog.search_templates',
+ 'Staff Catalog Search Templates',
+ 'cwst', 'label'
+ )
+);
+
+COMMIT;
+