<div class="col-lg-7 d-flex">
<button class="btn btn-outline-dark mr-2" (click)="applyTemplate()" i18n>Apply</button>
<button class="btn btn-outline-dark mr-2" (click)="saveTemplate()" i18n>Save</button>
+ <!--
<button class="btn btn-outline-dark mr-2" (click)="importTemplate()" i18n>Import</button>
+ -->
- <button class="btn btn-outline-dark mr-2" (click)="importTemplate()" i18n>Import</button>
+ <!--
+ The type typical approach of wrapping a file input in a <label>
+ results in button-ish things that have slightly different dimensions
+ -->
+ <button class="btn btn-outline-dark mr-2" (click)="templateFile.click()">
+ <input type="file" class="d-none" #templateFile
+ (change)="importTemplate($event)" id="template-file-upload"/>
+ <span i18n>Import</span>
+ </button>
<a (click)="exportTemplate($event)"
download="export_copy_template.json" [href]="exportTemplateUrl()">
this.store.setLocalItem('cat.copy.last_template', entry.id);
- // TODO: handle owning_lib and statcats differently.
+ // TODO: handle owning_lib and statcats differently, location
const template = this.volcopy.templates[entry.id];
if (value === null || value === undefined) { return; }
- this.applyCopyValue(field, value);
+ // In some cases, we may have to fetch the data since
+ // the local code assumes copy field is fleshed.
+ let promise = Promise.resolve(value);
- // Indicate in the form these values have changed
- this.batchAttrs
- .filter(ba => ba.name === field)
- .forEach(attr => attr.hasChanged = true);
+ // TODO: promises in loops are dangerous becuase they
+ // can lead to blasts of duplicate requests for non-local
+ // data. Consider an alternative approach.
+
+ if (field === 'location') {
+ // May be a 'remote' location. Fetch as needed.
+ promise = this.volcopy.getLocation(value);
+ }
+
+ promise.then(val => {
+ this.applyCopyValue(field, val);
+
+ // Indicate in the form these values have changed
+ this.batchAttrs
+ .filter(ba => ba.name === field)
+ .forEach(attr => attr.hasChanged = true);
+ });
});
}
if (entry.freetext) {
name = entry.label;
+ // freetext entries don't have an ID, but we may need one later.
+ entry.id = entry.label;
template = {};
} else {
name = entry.id;
const value = copy[comp.name]();
if (value === null) {
delete template[comp.name];
+
} else {
- template[comp.name] = value;
+ // some values are fleshed.
+ // this assumes fleshed objects have an 'id' value,
+ // which is true so far.
+ template[comp.name] =
+ typeof value === 'object' ? value.id() : value;
}
}
});
- console.debug('Saving template', template);
-
this.volcopy.templates[name] = template;
this.volcopy.saveTemplates();
}
exportTemplate($event) {
if (this.fileExport.inProgress()) { return; }
- const entry: ComboboxEntry = this.copyTemplateCbox.selected;
- if (!entry) { return; }
+ this.fileExport.exportFile(
+ $event, JSON.stringify(this.volcopy.templates), 'text/json');
+ }
- const template = this.volcopy.templates[entry.id];
+ importTemplate($event) {
+ const file: File = $event.target.files[0];
+ if (!file) { return; }
- this.fileExport.exportFile(
- $event, JSON.stringify(template), 'text/json');
+ const reader = new FileReader();
+
+ reader.addEventListener('load', () => {
+
+ try {
+ const template = JSON.parse(reader.result as string);
+ const name = Object.keys(template)[0];
+ this.volcopy.templates[name] = template[name];
+ } catch (E) {
+ console.error('Invalid Item Attribute template', E);
+ return;
+ }
+
+ this.volcopy.saveTemplates();
+ // Adds the new one to the list and re-sorts the labels.
+ this.volcopy.fetchTemplates();
+ });
+
+ reader.readAsText(file);
}
// Returns null when no export is in progress.
return this.fileExport.safeUrl;
}
+ deleteTemplate() {
+ const entry: ComboboxEntry = this.copyTemplateCbox.selected;
+ if (!entry) { return; }
+ delete this.volcopy.templates[entry.id];
+ this.volcopy.saveTemplates();
+ this.copyTemplateCbox.selected = null;
+ }
+
displayAttr(field: string): boolean {
return this.volcopy.defaults.hidden[field] !== true;
}
autoId = -1;
+ localOrgs: number[];
defaults: VolCopyDefaults = null;
defaultLocation: IdlObject;
copyStatuses: {[id: number]: IdlObject} = null;
+ // This will be all 'local' copy locations plus any remote
+ // locations that we are required to interact with.
+ copyLocationMap: {[id: number]: IdlObject} = {};
+
// Track this here so it can survive route changes.
currentContext: VolCopyContext;
if (this.itemTypeMaps.length > 0) { return Promise.resolve(); }
- const myOrgs = this.org.fullPath(this.auth.user().ws_ou(), true);
+ this.localOrgs = this.org.fullPath(this.auth.user().ws_ou(), true);
return this.fetchDefaults()
+ .then(_ => this.getLocations())
.then(_ => this.holdings.fetchCallNumberClasses())
.then(cls => this.volClasses = cls)
return this.holdings.fetchCallNumberPrefixes().then(prefixes =>
this.volPrefixes = prefixes
.filter(sfx => sfx.id() !== -1)
- .filter(pfx => myOrgs.includes(pfx.owning_lib()))
+ .filter(pfx => this.localOrgs.includes(pfx.owning_lib()))
);
})
return this.holdings.fetchCallNumberSuffixes().then(suffixes =>
this.volSuffixes = suffixes
.filter(sfx => sfx.id() !== -1)
- .filter(sfx => myOrgs.includes(sfx.owning_lib()))
+ .filter(sfx => this.localOrgs.includes(sfx.owning_lib()))
);
})
});
}
+ getLocation(id: number): Promise<IdlObject> {
+ if (this.copyLocationMap[id]) {
+ return Promise.resolve(this.copyLocationMap[id]);
+ }
+
+ return this.pcrud.retrieve('acpl', id)
+ .pipe(tap(loc => this.copyLocationMap[loc.id()] = loc))
+ .toPromise();
+ }
+
+ getLocations(): Promise<any> {
+ this.localOrgs = this.org.fullPath(this.auth.user().ws_ou(), true);
+
+ return this.pcrud.search('acpl',
+ {deleted: 'f', owning_lib: this.localOrgs},
+ {order_by: {acpl: 'name'}}
+ ).pipe(tap(loc => this.copyLocationMap[loc.id()] = loc)
+ ).toPromise();
+ }
+
fetchTemplates(): Promise<any> {
// TODO: copy templates should be server settings
// Use the first non-deleted copy location within org unit
// range as the default. Typically "Stacks".
- const myOrgs = this.org.fullPath(this.auth.user().ws_ou(), true);
+ this.localOrgs = this.org.fullPath(this.auth.user().ws_ou(), true);
return this.pcrud.search('acpl',
- {deleted: 'f', owning_lib: myOrgs},
+ {deleted: 'f', owning_lib: this.localOrgs},
{order_by: {acpl: 'id'}, limit: 1}
).toPromise().then(loc => this.defaultLocation = loc);
});