ctx.org.root().ou_type().depth() :
ctx.searchOrg.ou_type().depth();
- // Term search, looking for metarecords, but no
- // specific metarecord has been selected for display.
- const isMeta = (
- ctx.termSearch.isSearchable() &&
- ctx.termSearch.groupByMetarecord &&
- !ctx.termSearch.fromMetarecord
- );
+ const isMeta = ctx.termSearch.isMetarecordSearch();
let observable: Observable<BibRecordSummary>;
CATALOG_CCVM_FILTERS.forEach(code => this.ccvmFilters[code] = ['']);
}
+ // True when grouping by metarecord but not when displaying the
+ // contents of a metarecord.
+ isMetarecordSearch(): boolean {
+ return (
+ this.isSearchable() &&
+ this.groupByMetarecord &&
+ this.fromMetarecord === null
+ );
+ }
+
isSearchable(): boolean {
return (
this.query[0] !== ''
<div class="col-lg-1" i18n>Override</div>
</div>
<div class="row mt-1 ml-1 mr-1" *ngFor="let ctx of holdContexts">
- <ng-container *ngIf="ctx.holdMeta">
- <div class="col-lg-1">
- <ng-container *ngFor="let code of ctx.holdMeta.bibSummary.attributes.icon_format">
- <img class="pr-1"
- alt="{{iconFormatLabel(code)}}"
- title="{{iconFormatLabel(code)}}"
- src="/images/format_icons/icon_format/{{code}}.png"/>
- </ng-container>
- </div>
- <div class="col-lg-3">
- <a routerLink="/staff/catalog/record/{{ctx.holdMeta.bibId}}">
- {{ctx.holdMeta.bibSummary.display.title}}
- </a>
- </div>
- <div class="col-lg-2">{{ctx.holdMeta.bibSummary.display.author}}</div>
- <div class="col-lg-2">
- <ng-container *ngIf="ctx.holdMeta.volume; else anyValue">
- {{ctx.holdMeta.volume.label()}}
- </ng-container>
- </div>
- <div class="col-lg-1">
- <ng-container *ngIf="ctx.holdMeta.copy; else anyValue">
- {{ctx.holdMeta.copy.barcode()}}
- </ng-container>
- </div>
- <div class="col-lg-2">
- <ng-container *ngIf="!ctx.lastRequest && !ctx.processing">
- <div class="alert alert-info" i18n>Hold Pending</div>
- </ng-container>
- <ng-container *ngIf="ctx.processing">
- <div class="alert alert-primary" i18n>Hold Processing...</div>
- </ng-container>
- <ng-container *ngIf="ctx.lastRequest">
- <ng-container *ngIf="ctx.lastRequest.result.success">
- <div class="alert alert-success" i18n>Hold Succeeded</div>
+ <div class="col-lg-12" *ngIf="ctx.holdMeta">
+ <div class="row">
+ <div class="col-lg-1">
+ <ng-container
+ *ngFor="let code of ctx.holdMeta.bibSummary.attributes.icon_format">
+ <img class="pr-1"
+ alt="{{iconFormatLabel(code)}}"
+ title="{{iconFormatLabel(code)}}"
+ src="/images/format_icons/icon_format/{{code}}.png"/>
+ </ng-container>
+ </div>
+ <!-- TODO: link for a metarecord should
+ jump to constituent bib list search page? -->
+ <div class="col-lg-3">
+ <a routerLink="/staff/catalog/record/{{ctx.holdMeta.bibId}}">
+ {{ctx.holdMeta.bibSummary.display.title}}
+ </a>
+ </div>
+ <div class="col-lg-2">{{ctx.holdMeta.bibSummary.display.author}}</div>
+ <div class="col-lg-2">
+ <ng-container *ngIf="ctx.holdMeta.volume; else anyValue">
+ {{ctx.holdMeta.volume.label()}}
+ </ng-container>
+ </div>
+ <div class="col-lg-1">
+ <ng-container *ngIf="ctx.holdMeta.copy; else anyValue">
+ {{ctx.holdMeta.copy.barcode()}}
+ </ng-container>
+ </div>
+ <div class="col-lg-2">
+ <ng-container *ngIf="!ctx.lastRequest && !ctx.processing">
+ <div class="alert alert-info" i18n>Hold Pending</div>
+ </ng-container>
+ <ng-container *ngIf="ctx.processing">
+ <div class="alert alert-primary" i18n>Hold Processing...</div>
</ng-container>
- <ng-container *ngIf="!ctx.lastRequest.result.success">
- <div class="alert alert-danger">
- {{ctx.lastRequest.result.evt.textcode}}
+ <ng-container *ngIf="ctx.lastRequest">
+ <ng-container *ngIf="ctx.lastRequest.result.success">
+ <div class="alert alert-success" i18n>Hold Succeeded</div>
+ </ng-container>
+ <ng-container *ngIf="!ctx.lastRequest.result.success">
+ <div class="alert alert-danger">
+ {{ctx.lastRequest.result.evt.textcode}}
+ </div>
+ </ng-container>
+ </ng-container>
+ </div>
+ <div class="col-lg-1">
+ <ng-container *ngIf="canOverride(ctx)">
+ <button class="btn btn-info" (click)="override(ctx)">Override</button>
+ </ng-container>
+ </div>
+ </div>
+ <!-- note: using inline style since class-level styling for rows
+ is superseded by the striped-even styling of the container -->
+ <div class="row" *ngIf="hasMetaFilters(ctx)"
+ style="background-color:inherit; border:none">
+ <div class="col-lg-1"><label i18n>Formats: </label></div>
+ <div class="col-lg-11 d-flex">
+ <ng-container
+ *ngFor="let ccvm of ctx.holdMeta.metarecord_filters.formats">
+ <div class="form-check ml-3">
+ <input class="form-check-input" type="checkbox"
+ [disabled]="ctx.holdMeta.metarecord_filters.formats.length == 1"
+ [(ngModel)]="ctx.selectedFormats.formats[ccvm.code()]"/>
+ <img class="ml-1"
+ alt="{{iconFormatLabel(ccvm.code())}}"
+ title="{{iconFormatLabel(ccvm.code())}}"
+ src="/images/format_icons/icon_format/{{ccvm.code()}}.png"/>
+ <label class="form-check-label ml-1">
+ {{ccvm.search_label() || ccvm.value()}}
+ </label>
</div>
</ng-container>
- </ng-container>
+ </div>
</div>
- <div class="col-lg-1">
- <ng-container *ngIf="canOverride(ctx)">
- <button class="btn btn-info" (click)="override(ctx)">Override</button>
- </ng-container>
+ <div class="row" *ngIf="hasMetaFilters(ctx)"
+ style="background-color:inherit; border:none">
+ <div class="col-lg-1"><label i18n>Languages: </label></div>
+ <div class="col-lg-11 d-flex">
+ <ng-container
+ *ngFor="let ccvm of ctx.holdMeta.metarecord_filters.langs">
+ <div class="form-check ml-3">
+ <input class="form-check-input" type="checkbox"
+ [disabled]="ctx.holdMeta.metarecord_filters.langs.length == 1"
+ [(ngModel)]="ctx.selectedFormats.langs[ccvm.code()]"/>
+ <label class="form-check-label ml-1">
+ {{ccvm.search_label() || ccvm.value()}}
+ </label>
+ </div>
+ </ng-container>
+ </div>
</div>
- </ng-container>
+ </div>
</div>
</div>
lastRequest: HoldRequest;
canOverride?: boolean;
processing: boolean;
+ selectedFormats: any;
+
+ constructor(target: number) {
+ this.holdTarget = target;
+ this.processing = false;
+ this.selectedFormats = {
+ // code => selected-boolean
+ formats: {},
+ langs: {}
+ }
+ }
}
@Component({
this.pickupLib = this.auth.user().ws_ou();
this.holdContexts = this.holdTargets.map(target => {
- const ctx = new HoldContext();
- ctx.holdTarget = target;
+ const ctx = new HoldContext(target);
return ctx;
});
this.holds.getHoldTargetMeta(this.holdType, this.holdTargets)
.subscribe(meta => {
this.holdContexts.filter(ctx => ctx.holdTarget === meta.target)
- .forEach(ctx => ctx.holdMeta = meta);
+ .forEach(ctx => {
+ ctx.holdMeta = meta;
+ this.mrFiltersToSelectors(ctx);
+ });
});
}
+ // By default, all metarecord filters options are enabled.
+ mrFiltersToSelectors(ctx: HoldContext) {
+ if (this.holdType !== 'M') { return; }
+
+ const meta = ctx.holdMeta;
+ if (meta.metarecord_filters) {
+ if (meta.metarecord_filters.formats) {
+ meta.metarecord_filters.formats.forEach(
+ ccvm => ctx.selectedFormats.formats[ccvm.code()] = true);
+ }
+ if (meta.metarecord_filters.langs) {
+ meta.metarecord_filters.langs.forEach(
+ ccvm => ctx.selectedFormats.langs[ccvm.code()] = true);
+ }
+ }
+ }
+
+ // Map the selected metarecord filters optoins to a JSON-encoded
+ // list of attr filters as required by the API.
+ // Compiles a blob of
+ // {target: JSON({"0": [{_attr: ctype, _val: code}, ...], "1": [...]})}
+ // TODO: this should live in the hold service, not in the UI code.
+ mrSelectorsToFilters(ctx: HoldContext): {[target: number]: string} {
+
+ const meta = ctx.holdMeta;
+ const slf = ctx.selectedFormats;
+ const result: any = {};
+
+ const formats = Object.keys(slf.formats)
+ .filter(code => Boolean(slf.formats[code])); // user-selected
+
+ const langs = Object.keys(slf.langs)
+ .filter(code => Boolean(slf.langs[code])); // user-selected
+
+ const compiled: any = {};
+
+ if (formats.length > 0) {
+ compiled['0'] = [];
+ formats.forEach(code => {
+ const ccvm = meta.metarecord_filters.formats.filter(
+ format => format.code() === code)[0];
+ compiled['0'].push({
+ _attr: ccvm.ctype(),
+ _val: ccvm.code()
+ });
+ });
+ }
+
+ if (langs.length > 0) {
+ compiled['1'] = [];
+ langs.forEach(code => {
+ const ccvm = meta.metarecord_filters.langs.filter(
+ format => format.code() === code)[0];
+ compiled['1'].push({
+ _attr: ccvm.ctype(),
+ _val: ccvm.code()
+ });
+ });
+ }
+
+ if (Object.keys(compiled).length > 0) {
+ const result = {};
+ result[ctx.holdTarget] = JSON.stringify(compiled);
+ return result;
+ }
+
+ return null;
+ }
+
holdForChanged() {
this.user = null;
placeOneHold(ctx: HoldContext, override?: boolean): Promise<any> {
ctx.processing = true;
+ const selectedFormats = this.mrSelectorsToFilters(ctx);
return this.holds.placeHold({
holdTarget: ctx.holdTarget,
notifySms: this.notifySms ? this.smsValue : null,
smsCarrier: this.notifySms ? this.smsCarrier : null,
thawDate: this.suspend ? this.activeDate : null,
- frozen: this.suspend
+ frozen: this.suspend,
+ holdableFormats: selectedFormats
}).toPromise().then(
request => {
return this.cat.iconFormatLabel(code);
}
+ // TODO: for now, only show meta filters for meta holds.
+ // Add an "advanced holds" option to display these for T hold.
hasMetaFilters(ctx: HoldContext): boolean {
- return ctx.holdMeta.metarecord_filters && (
- ctx.holdMeta.metarecord_filters.langs.length > 1 ||
- ctx.holdMeta.metarecord_filters.formats.length > 1);
+ return (
+ this.holdType === 'M' && // TODO
+ ctx.holdMeta.metarecord_filters && (
+ ctx.holdMeta.metarecord_filters.langs.length > 1 ||
+ ctx.holdMeta.metarecord_filters.formats.length > 1
+ )
+ );
}
}
<div class="float-right">
<span>
<button (click)="placeHold()"
- [disabled]="summary.metabibId"
class="btn btn-sm btn-success label-with-material-icon small-text-1">
<span class="material-icons">check</span>
<span i18n>Place Hold</span>
}
placeHold(): void {
- this.router.navigate(['/staff/catalog/hold/T'],
- {queryParams: {target: this.summary.id}});
+ let holdType = 'T';
+ let holdTarget = this.summary.id;
+
+ const ts = this.searchContext.termSearch;
+ if (ts.isMetarecordSearch()) {
+ holdType = 'M';
+ holdTarget = this.summary.metabibId;
+ }
+
+ this.router.navigate([`/staff/catalog/hold/${holdType}`],
+ {queryParams: {target: holdTarget}});
}
addToList(): void {
smsCarrier?: string;
thawDate?: string; // ISO date
frozen?: boolean;
- holdableFormats?: {[id: number]: string[]};
+ holdableFormats?: {[target: number]: string};
result?: HoldRequestResult
};