import {Component, Input, OnInit, AfterViewInit, ViewChild,
- QueryList, ViewChildren} from '@angular/core';
+ EventEmitter, Output, QueryList, ViewChildren} from '@angular/core';
import {Router, ActivatedRoute, ParamMap} from '@angular/router';
import {SafeUrl} from '@angular/platform-browser';
import {tap} from 'rxjs/operators';
@ViewChildren(BatchItemAttrComponent)
batchAttrs: QueryList<BatchItemAttrComponent>;
+ // Emitted when the save-ability of this form changes.
+ @Output() canSaveChange: EventEmitter<boolean> = new EventEmitter<boolean>();
+
constructor(
private router: Router,
private route: ActivatedRoute,
this.fineLevelLabelMap[1] = this.fineLevelLow.text;
this.fineLevelLabelMap[2] = this.fineLevelNormal.text;
this.fineLevelLabelMap[3] = this.fineLevelHigh.text;
+
}
statCats(): IdlObject[] {
}
if (field === 'owning_lib') {
- return this.owningLibChanged(value, changeSelection);
- }
+ this.owningLibChanged(value, changeSelection);
- this.context.copyList().forEach(copy => {
- if (!copy[field] || copy[field]() === value) { return; }
+ } else {
- // Change selection indicates which items should be modified
- // based on the display value for the selected field at
- // time of editing.
- if (changeSelection &&
- !this.copyWantsChange(copy, field, changeSelection)) {
- return;
- }
+ this.context.copyList().forEach(copy => {
+ if (!copy[field] || copy[field]() === value) { return; }
- copy[field](value);
- copy.ischanged(true);
- });
+ // Change selection indicates which items should be modified
+ // based on the display value for the selected field at
+ // time of editing.
+ if (changeSelection &&
+ !this.copyWantsChange(copy, field, changeSelection)) {
+ return;
+ }
+
+ copy[field](value);
+ copy.ischanged(true);
+ });
+ }
+
+ this.emitSaveChange();
}
owningLibChanged(orgId: number, changeSelection?: BatchChangeSelection) {
openCopyAlerts() {
this.copyAlertsDialog.inPlaceMode = true;
- this.copyAlertsDialog.mode = 'create';
+ this.copyAlertsDialog.copyIds = this.context.copyList().map(c => c.id());
+
this.copyAlertsDialog.open({size: 'lg'}).subscribe(
newAlert => {
if (newAlert) {
this.store.setLocalItem('cat.copy.last_template', entry.id);
- // TODO: handle owning_lib
-
const template = this.volcopy.templates[entry.id];
Object.keys(template).forEach(field => {
}
return true;
}
+
+ emitSaveChange() {
+
+ // Timeout allows the digest cycle which created the change to complete.
+ setTimeout(() => {
+
+ const canSave = this.batchAttrs.filter(
+ attr => attr.warnOnRequired()).length === 0;
+
+ this.canSaveChange.emit(canSave)
+ });
+ }
}
-import {Component, OnInit, AfterViewInit, ViewChild, Input, Renderer2} from '@angular/core';
+import {Component, OnInit, AfterViewInit, ViewChild, Input, Renderer2, Output, EventEmitter} from '@angular/core';
import {Router, ActivatedRoute, ParamMap} from '@angular/router';
import {tap} from 'rxjs/operators';
import {IdlService, IdlObject} from '@eg/core/idl.service';
// There are 10 columns in the editor form. Set the flex values
// here so they don't have to be hard-coded and repeated in the
// markup. Changing a flex value here will propagate to all
- // rows in the form.
+ // rows in the form. Column numbers are 1-based.
flexSettings: {[column: number]: number} = {
1: 1, 2: 1, 3: 2, 4: 1, 5: 2, 6: 1, 7: 1, 8: 2, 9: 1, 10: 1};
@ViewChild('confirmDelCopy', {static: false})
confirmDelCopy: ConfirmDialogComponent;
+ // Emitted when the save-ability of this form changes.
+ @Output() canSaveChange: EventEmitter<boolean> = new EventEmitter<boolean>();
+
constructor(
private renderer: Renderer2,
private idl: IdlService,
vol[key](value);
vol.ischanged(true);
}
+
+ this.emitSaveChange();
}
applyCopyValue(copy: IdlObject, key: string, value: any) {
copy.ischanged(true);
copy._dupe_barcode = false;
- if (barcode && !this.autoBarcodeInProgress) {
+ if (!barcode) {
+ this.emitSaveChange();
+ return;
+ }
+
+ if (!this.autoBarcodeInProgress) {
// Manual barcode entry requires dupe check
copy._dupe_barcode = false;
deleted: 'f',
barcode: barcode,
id: {'!=': copy.id()}
- }).subscribe(resp => {
- if (resp) { copy._dupe_barcode = true; }
- });
+ }).subscribe(
+ resp => {
+ if (resp) { copy._dupe_barcode = true; }
+ },
+ err => {},
+ () => this.emitSaveChange()
+ );
}
}
this.volcopy.defaults.values.use_checkdigit = this.useCheckdigit === true;
this.volcopy.saveDefaults();
}
+
+ canSave(): boolean {
+
+ const copies = this.context.copyList();
+
+ const badCopies = copies.filter(copy => {
+ return copy._dupe_barcode || (!copy.isnew() && !copy.barcode());
+ }).length > 0;
+
+ if (badCopies) { return false; }
+
+ const badVols = this.context.volNodes().filter(volNode => {
+ const vol = volNode.target;
+ return !(
+ vol.prefix() && vol.label() && vol.suffix && vol.label_class()
+ );
+ }).length > 0;
+
+ return !badVols;
+ }
+
+ emitSaveChange() {
+
+ // Timeout allows the digest cycle which created the change to complete.
+ setTimeout(() => {
+ this.canSaveChange.emit(this.canSave());
+ });
+ }
}
<li ngbNavItem="holdings">
<a ngbNavLink i18n>Holdings</a>
<ng-template ngbNavContent>
- <div class="mt-2"><eg-vol-edit [context]="context"></eg-vol-edit></div>
+ <div class="mt-2">
+ <eg-vol-edit [context]="context"
+ (canSaveChange)="volsCanSave = $event"></eg-vol-edit>
+ </div>
<ng-container *ngIf="volcopy.defaults.values.unified_display">
- <div class="mt-2"><eg-copy-attrs [context]="context"></eg-copy-attrs></div>
+ <div class="mt-2">
+ <eg-copy-attrs [context]="context"
+ (canSaveChange)="attrsCanSave = $event"></eg-copy-attrs>
+ </div>
</ng-container>
</ng-template>
</li>
<li ngbNavItem="attrs">
<a ngbNavLink i18n>Item Attributes</a>
<ng-template ngbNavContent>
- <div class="mt-2"><eg-copy-attrs [context]="context"></eg-copy-attrs></div>
+ <div class="mt-2">
+ <eg-copy-attrs [context]="context"
+ (canSaveChange)="attrsCanSave = $event"></eg-copy-attrs>
+ </div>
</ng-template>
</li>
</ng-container>
i18n>Print Labels?</label>
</div>
<div class="flex-1"> </div>
- <button class="btn btn-outline-dark" (click)="save()" i18n>Save</button>
- <button class="btn btn-outline-dark ml-2"
- (click)="save(true)" i18n>Save & Exit</button>
+ <button class="btn btn-outline-dark" (click)="save()"
+ [ngClass]="{'border-danger': isNotSaveable()}"
+ [disabled]="isNotSaveable()" i18n>Save</button>
+ <button class="btn btn-outline-dark ml-2" (click)="save(true)"
+ [ngClass]="{'border-danger': isNotSaveable()}"
+ [disabled]="isNotSaveable()" i18n>Save & Exit</button>
</div>
</div>
</ng-container>
target: string; // item | callnumber | record | session
targetId: string; // id value or session string
+ volsCanSave = true;
+ attrsCanSave = true;
+
constructor(
private router: Router,
private route: ActivatedRoute,
this.context.recordId = editSession.record_id;
- // These are currently ignored, since visibility is tab-based
- this.context.hideVols = editSession.hide_vols === true;
- this.context.hideCopies = editSession.hide_copies === true;
-
if (editSession.copies && editSession.copies.length > 0) {
return this.fetchCopies(editSession.copies);
}
setTimeout(() => window.open(url, '_blank'));
});
}
+
+ isNotSaveable(): boolean {
+ return !(this.volsCanSave && this.attrsCanSave);
+ }
}
/* Managing volcopy data */
-
-
interface VolCopyDefaults {
values: {[field: string]: any};
hidden: {[field: string]: boolean};
volsToDelete: IdlObject[] = [];
copiesToDelete: IdlObject[] = [];
- hideVols: boolean;
- hideCopies: boolean;
-
reset() {
this.holdings = new HoldingsTree();
this.volsToDelete = [];
o1.target.shortname() < o2.target.shortname() ? -1 : 1);
}
- // Changes pending and no unresolved issues.
- isSaveable(): boolean {
- const dupeBc = this.copyList().filter(c => c._dupe_barcode).length;
-
- if (dupeBc) { return false; }
-
+ changesPending(): boolean {
const modified = (o: IdlObject): boolean => {
return o.isnew() || o.ischanged() || o.isdeleted();
};
<div class="modal-header">
<h4 class="modal-title">
<ng-container *ngIf="mode == 'create'">
- <span i18n>Adding alerts for {{copies.length}} item(s).</span>
+ <span i18n>Adding alerts for {{copyIds.length}} item(s).</span>
</ng-container>
<ng-container *ngIf="mode == 'manage'">
<span i18n>Managing alerts for item {{copies[0].barcode()}}</span>
export class CopyAlertsDialogComponent
extends DialogComponent implements OnInit {
- _copyIds: number[];
- @Input() set copyIds(ids: number[]) {
- this._copyIds = [].concat(ids);
- }
- get copyIds(): number[] {
- return this._copyIds;
- }
+ // If there are multiple copyIds, only new alerts may be applied.
+ // If there is only one copyId, then tags may be applied or removed.
+ @Input() copyIds: number[] = [];
- _mode: string; // create | manage
- @Input() set mode(m: string) {
- this._mode = m;
- }
- get mode(): string {
- return this._mode;
- }
+ mode: string; // create | manage
// If true, no attempt is made to save the new alerts to the
// database. It's assumed this takes place in the calling code.
copies: IdlObject[];
// In 'manage' mode we only handle a single copy.
copy: IdlObject;
+
alertTypes: ComboboxEntry[];
newAlert: IdlObject;
changesMade: boolean;
}
// In manage mode, we can only manage a single copy.
- // But in create mode, we can add alerts to multiple copies.
-
- if (this.mode === 'manage') {
- if (this.copyIds.length > 1) {
- console.warn('Attempt to manage alerts for multiple copies.');
- this.copyIds = [this.copyIds[0]];
- }
+ // But in create mode, we can add tags to multiple copies.
+ if (this.copyIds.length === 1 && !this.inPlaceMode) {
+ this.mode = 'manage';
+ } else {
+ this.mode = 'create';
}
// Observerify data loading
}
getAlertTypes(): Promise<any> {
- if (this.alertTypes) {
- return Promise.resolve();
- }
+ if (this.alertTypes) { return Promise.resolve(); }
+
return this.pcrud.retrieveAll('ccat',
{ active: true,
scope_org: this.org.ancestors(this.auth.user().ws_ou(), true)
}
getCopies(): Promise<any> {
-
if (this.inPlaceMode) { return Promise.resolve(); }
return this.pcrud.search('acp', {id: this.copyIds}, {}, {atomic: true})