</div>
</div>
-<div class="row pb-2 pt-2" *ngIf="dupeSearches.length > 0">
+<div class="row pb-1 pt-1" *ngIf="dupesFound().length > 0">
<div class="col-lg-12 d-flex">
<div class="ml-auto mr-2">
- <div *ngFor="let dupe of dupeSearches"
- class="alert alert-danger ml-auto p-2 mt-2" i18n>
- {{dupe.count}} patron(s) with same
- <ng-container [ngSwitch]="dupe.category">
- <span *ngSwitchCase="'phone'">phone</span>
- <span *ngSwitchCase="'email'">email</span>
- <span *ngSwitchCase="'name'">name</span>
- <span *ngSwitchCase="'ident'">identifier</span>
- <span *ngSwitchCase="'address'">address</span>
- </ng-container>
+ <div *ngFor="let dupe of dupesFound()"
+ class="alert alert-danger ml-auto p-2 mt-2">
+ <a routerLink="/staff/circ/patron/search"
+ target="_blank" [queryParams]="{search: dupe.json}" i18n>
+ {{dupe.count}} patron(s) with same
+ <ng-container [ngSwitch]="dupe.category">
+ <span *ngSwitchCase="'phone'">phone</span>
+ <span *ngSwitchCase="'email'">email</span>
+ <span *ngSwitchCase="'name'">name</span>
+ <span *ngSwitchCase="'ident'">identifier</span>
+ <span *ngSwitchCase="'address'">address</span>
+ </ng-container>
+ </a>
</div>
</div>
</div>
category: SearchCategory;
count: number;
search: PatronSearchFieldSet;
+ json: string;
}
@Component({
saveCloneClicked: EventEmitter<void> = new EventEmitter<void>();
printClicked: EventEmitter<void> = new EventEmitter<void>();
- dupeSearches: DupeSearch[] = [];
+ searches: {[category: string]: DupeSearch} = {};
constructor(
private org: OrgService,
this.visibilityLevel = v;
}
+ dupesFound(): DupeSearch[] {
+ return Object.values(this.searches).filter(dupe => dupe.count > 0);
+ }
+
+
+
checkDupes(category: string, search: PatronSearchFieldSet) {
this.net.request(
true // as id
).subscribe(ids => {
ids = ids.filter(id => Number(id) !== this.patronId);
- const count = ids.length;
-
- if (count > 0) {
- const existing =
- this.dupeSearches.filter(s => s.category === category)[0];
- if (existing) {
- existing.search = search;
- existing.count = count;
- } else {
- this.dupeSearches.push({
- category: category as SearchCategory,
- search: search,
- count: count
- });
- }
- } else {
- this.dupeSearches =
- this.dupeSearches.filter(s => s.category !== category);
- }
+ this.searches[category] = {
+ category: category as SearchCategory,
+ count: ids.length,
+ search: search,
+ json: JSON.stringify(search)
+ };
});
}
}
</div>
</div>
+<ng-template #fieldExample let-args="args">
+ <span class="ml-2" *ngIf="exampleText(args.cls, args.field)" i18n>
+ Example: {{exampleText(args.cls, args.field)}}
+ </span>
+</ng-template>
+
<!-- IDL-generated field labels. Override with args.overrideLabel -->
<ng-template #fieldLabel let-args="args">
<div class="col-lg-3 field-label">
<label for="{{getClass(args.cls)}}-{{args.field}}-input">
{{getFieldLabel(getClass(args.cls), args.field, args.overrideLabel)}}
</label>
- <!-- TODO doc links -->
+ <eg-help-popover *ngIf="getFieldDoc(args.cls, args.field)"
+ buttonClass="btn-sm text-info p-0 m-0"
+ [placement]="'bottom'" [helpText]="getFieldDoc(args.cls, args.field)">
+ </eg-help-popover>
</div>
</ng-template>
</ng-container>
<ng-container *ngTemplateOutlet="args.template; context: {args: args}">
</ng-container>
+ <ng-container *ngTemplateOutlet="fieldExample; context: {args: args}">
+ </ng-container>
</div>
</ng-template>
<button class="btn btn-outline-dark"
(click)="invalidateField('day_phone')" i18n>Invalidate</button>
</ng-container>
+ <ng-container *ngTemplateOutlet="fieldExample; context: {args: {field: 'day_phone'}}">
+ </ng-container>
</div>
</div>
holdNotifyTypes: {email?: boolean, phone?: boolean, sms?: boolean} = {};
+ fieldDoc: {[cls: string]: {[field: string]: string}} = {};
+
constructor(
private org: OrgService,
private net: NetService,
load(): Promise<any> {
this.loading = true;
return this.setStatCats()
+ .then(_ => this.getFieldDocs())
.then(_ => this.setSurveys())
.then(_ => this.loadPatron())
.then(_ => this.getSecondaryGroups())
.then(_ => this.loading = false);
}
- setupToolbar() {
+ getFieldDocs(): Promise<any> {
+ return this.pcrud.search('fdoc', {
+ fm_class: ['au', 'ac', 'aua', 'actsc', 'asv', 'asvq', 'asva']})
+ .pipe(tap(doc => {
+ if (!this.fieldDoc[doc.fm_class()]) {
+ this.fieldDoc[doc.fm_class()] = {};
+ }
+ this.fieldDoc[doc.fm_class()][doc.field()] = doc.string();
+ console.log(this.fieldDoc);
+ })).toPromise();
+ }
+
+ getFieldDoc(cls: string, field: string): string {
+ cls = this.getClass(cls);
+ if (this.fieldDoc[cls]) {
+ return this.fieldDoc[cls][field];
+ }
+ }
+
+ exampleText(cls: string, field: string): string {
+ cls = this.getClass(cls);
+ return this.context.settingsCache[`ui.patron.edit.${cls}.${field}.example`];
}
setSurveys(): Promise<any> {
import {Component, Input, Output, OnInit, AfterViewInit,
- EventEmitter, ViewChild, Renderer2} from '@angular/core';
+ EventEmitter, ViewChild} from '@angular/core';
+import {ActivatedRoute, ParamMap} from '@angular/router';
import {Observable, of} from 'rxjs';
import {map} from 'rxjs/operators';
import {IdlObject} from '@eg/core/idl.service';
export interface PatronSearch {
search: PatronSearchFieldSet;
- orgId: number;
+ orgId?: number;
}
@Component({
profileGroups: IdlObject[] = [];
constructor(
- private renderer: Renderer2,
+ private route: ActivatedRoute,
private net: NetService,
public org: OrgService,
private auth: AuthService,
}
ngOnInit() {
+
+ this.route.queryParamMap.subscribe((params: ParamMap) => {
+ const search = params.get('search');
+ if (search) {
+ try {
+ this.startWithSearch = {search: JSON.parse(search)};
+ } catch (E) {
+ console.error("Invalid JSON search value", search, E);
+ }
+ }
+ });
+
this.searchOrg = this.org.root();
this.store.getItemBatch([EXPAND_FORM, INCLUDE_INACTIVE])
.then(settings => {
}
ngAfterViewInit() {
- this.renderer.selectRootElement('#focus-this-input').focus();
+ const node = document.getElementById('focus-this-input');
+ if (node) { node.focus(); }
}
toggleExpandForm() {