import {BoolDisplayComponent} from '@eg/share/util/bool.component';
import {ToastService} from '@eg/share/toast/toast.service';
import {ToastComponent} from '@eg/share/toast/toast.component';
-import {StringComponent} from '@eg/share/string/string.component';
-import {StringService} from '@eg/share/string/string.service';
+import {StringModule} from '@eg/share/string/string.module';
@NgModule({
ProgressInlineComponent,
ProgressDialogComponent,
ToastComponent,
- StringComponent,
BoolDisplayComponent
],
imports: [
ReactiveFormsModule,
RouterModule,
NgbModule,
- EgCoreModule
+ EgCoreModule,
+ StringModule
],
exports: [
CommonModule,
NgbModule,
FormsModule,
EgCoreModule,
+ StringModule,
ReactiveFormsModule,
PrintComponent,
DialogComponent,
ProgressInlineComponent,
ProgressDialogComponent,
BoolDisplayComponent,
- ToastComponent,
- StringComponent
+ ToastComponent
]
})
providers: [
HatchService,
PrintService,
- StringService,
ToastService
]
};
--- /dev/null
+import {Component, Input, Output, EventEmitter, Host, OnInit} from '@angular/core';
+import {FmRecordEditorComponent} from './fm-editor.component';
+
+@Component({
+ selector: 'eg-fm-record-editor-action',
+ template: '<ng-template></ng-template>' // no-op
+})
+
+export class FmRecordEditorActionComponent implements OnInit {
+
+ // unique identifier
+ @Input() key: string;
+
+ @Input() label: string;
+
+ @Input() buttonCss = 'btn-outline-dark';
+
+ // Emits the 'key' of the clicked action.
+ @Output() actionClick: EventEmitter<string>;
+
+ @Input() disabled: boolean;
+
+ constructor(@Host() private editor: FmRecordEditorComponent) {
+ this.actionClick = new EventEmitter<string>();
+ }
+
+ ngOnInit() {
+ this.editor.actions.push(this);
+ }
+}
+
<eg-string #successStr text="Update Succeeded" i18n-text></eg-string>
<eg-string #failStr text="Update Failed" i18n-text></eg-string>
+<eg-confirm-dialog #confirmDel
+ dialogTitle="Delete?" i18n-dialogTitle
+ dialogBody="Delete {{recordLabel}}?" i18n-dialogBody>
+</eg-confirm-dialog>
+
<ng-template #dialogContent>
- <div class="modal-header bg-info">
+ <div class="modal-header bg-info" *ngIf="!hideBanner">
<h4 class="modal-title" i18n>Record Editor: {{recordLabel}}</h4>
<ng-container *ngIf="isDialog()">
<button type="button" class="close"
[limitPerms]="modePerms[mode]"
[readOnly]="field.readOnly"
[applyDefault]="field.orgDefaultAllowed"
- [initialOrgId]="record[field.name]()"
+ [applyOrgId]="record[field.name]()"
(onChange)="record[field.name]($event)">
</eg-org-select>
</ng-container>
[required]="field.isRequired()"
[entries]="field.linkedValues"
[asyncDataSource]="field.linkedValuesSource"
- [startId]="record[field.name]()"
+ [selectedId]="record[field.name]()"
(onChange)="record[field.name]($event ? $event.id : null)">
</eg-combobox>
</ng-container>
</form>
</div>
<div class="modal-footer">
+ <button type="button" class="btn {{action.buttonCss}}"
+ *ngFor="let action of actions" [disabled]="action.disabled"
+ (click)="action.actionClick.emit({action: action.key, record: record})">
+ {{action.label}}
+ </button>
<ng-container *ngIf="isDialog()">
<button type="button" class="btn btn-success" *ngIf="mode == 'view'"
(click)="closeEditor()" i18n>Close</button>
<button type="button" class="btn btn-warning ml-2" *ngIf="mode != 'view'"
(click)="cancel()" i18n>Cancel</button>
</ng-container>
+
+ <ng-container *ngIf="showDelete && mode != 'view'">
+ <button type="button" class="btn btn-warning" (click)="remove()"
+ [disabled]="record && record.isnew()" i18n>Delete</button>
+ </ng-container>
+
<button type="button" class="btn btn-info"
[disabled]="fmEditForm.invalid" *ngIf="mode != 'view'"
(click)="save()" i18n>Save</button>
import {StringComponent} from '@eg/share/string/string.component';
import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
-import {TranslateComponent} from '@eg/staff/share/translate/translate.component';
import {FormatService} from '@eg/core/format.service';
+import {TranslateComponent} from '@eg/share/translate/translate.component';
+import {FmRecordEditorActionComponent} from './fm-editor-action.component';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
interface CustomFieldTemplate {
template: TemplateRef<any>;
// IDL class hint (e.g. "aou")
@Input() idlClass: string;
- recId: any;
-
// Show datetime fields in this particular timezone
timezone: string = this.format.wsOrgTimezone;
- // IDL record we are editing
- record: IdlObject;
-
// Permissions extracted from the permacrud defs in the IDL
// for the current IDL class
modePerms: {[mode: string]: string};
// Display within a modal dialog window or inline in the page.
@Input() displayMode: 'dialog' | 'inline' = 'dialog';
+ // Hide the top 'Record Editor: ...' banner. Primarily useful
+ // for displayMode === 'inline'
+ @Input() hideBanner: boolean;
+
+ // Emit the modified object when the save action completes.
+ @Output() recordSaved = new EventEmitter<IdlObject>();
+
// Emit the modified object when the save action completes.
- @Output() onSave$ = new EventEmitter<IdlObject>();
+ @Output() recordDeleted = new EventEmitter<IdlObject>();
// Emit the original object when the save action is canceled.
- @Output() onCancel$ = new EventEmitter<IdlObject>();
+ @Output() recordCanceled = new EventEmitter<IdlObject>();
// Emit an error message when the save action fails.
- @Output() onError$ = new EventEmitter<string>();
+ @Output() recordError = new EventEmitter<string>();
@ViewChild('translator') private translator: TranslateComponent;
@ViewChild('successStr') successStr: StringComponent;
@ViewChild('failStr') failStr: StringComponent;
+ @ViewChild('confirmDel') confirmDel: ConfirmDialogComponent;
// IDL info for the the selected IDL class
idlDef: any;
// 'view' for viewing an existing record without editing
@Input() mode: 'create' | 'update' | 'view' = 'create';
- // Record ID to view/update. Value is dynamic. Records are not
- // fetched until .open() is called.
- @Input() set recordId(id: any) {
- if (id) { this.recId = id; }
- }
-
// custom function for munging the record before it gets saved;
// will get passed mode and the record itself
@Input() preSave: Function;
+ // recordId and record getters and setters.
+ // Note that setting the this.recordId to NULL does not clear the
+ // current value of this.record and vice versa. Only viable data
+ // is actionable. This allows the caller to use both @Input()'s
+ // without each clobbering the other.
+
+ // Record ID to view/update.
+ _recordId: any = null;
+ @Input() set recordId(id: any) {
+ if (id) {
+ if (id !== this._recordId) {
+ this._recordId = id;
+ this._record = null; // force re-fetch
+ this.handleRecordChange();
+ }
+ } else {
+ this._recordId = null;
+ }
+ }
+
+ get recordId(): any {
+ return this._recordId;
+ }
+
+ // IDL record we are editing
+ _record: IdlObject = null;
+ @Input() set record(r: IdlObject) {
+ if (r) {
+ if (!this.idl.pkeyMatches(this.record, r)) {
+ this._record = r;
+ this._recordId = null; // avoid mismatch
+ this.handleRecordChange();
+ }
+ } else {
+ this._record = null;
+ }
+ }
+
+ get record(): IdlObject {
+ return this._record;
+ }
+
+ actions: FmRecordEditorActionComponent[] = [];
+
+ initDone: boolean;
+
+ // Comma-separated list of field names defining the order in which
+ // fields should be rendered in the form. Any fields not represented
+ // will be rendered alphabetically by label after the named fields.
+ @Input() fieldOrder: string;
+
+ // When true, show a delete button and support delete operations.
+ @Input() showDelete: boolean;
+
constructor(
private modal: NgbModal, // required for passing to parent
private idl: IdlService,
} else {
this.initRecord();
}
+ this.initDone = true;
+ }
+
+ // If the record ID changes after ngOnInit has been called
+ // and we're using displayMode=inline, force the data to
+ // resync in real time
+ handleRecordChange() {
+ if (this.initDone && !this.isDialog()) {
+ this.initRecord();
+ }
}
open(args?: NgbModalOptions): Observable<any> {
return this.displayMode === 'dialog';
}
- // Set the record value and clear the recId value to
- // indicate the record is our current source of data.
+ // DEPRECATED: This is a duplicate of this.record = abc;
setRecord(record: IdlObject) {
- this.record = record;
- this.recId = null;
+ console.warn('fm-editor:setRecord() is deprecated. ' +
+ 'Use editor.record = abc or [record]="abc" instead');
+ this.record = record; // this calls the setter
}
// Translate comma-separated string versions of various inputs
if (this.mode === 'update' || this.mode === 'view') {
let promise;
- if (this.record && this.recId === null) {
+ if (this.record && this.recordId === null) {
promise = Promise.resolve(this.record);
- } else {
+ } else if (this.recordId) {
promise =
- this.pcrud.retrieve(this.idlClass, this.recId).toPromise();
+ this.pcrud.retrieve(this.idlClass, this.recordId).toPromise();
+ } else {
+ // Not enough data yet to fetch anything
+ return Promise.resolve();
}
return promise.then(rec => {
if (!rec) {
return Promise.reject(`No '${this.idlClass}'
- record found with id ${this.recId}`);
+ record found with id ${this.recordId}`);
}
- this.record = rec;
+ // Set this._record (not this.record) to avoid loop in initRecord()
+ this._record = rec;
this.convertDatatypesToJs();
return this.getFieldList();
});
//
// Create a new record from the stub record provided by the
// caller or a new from-scratch record
- this.setRecord(this.record || this.idl.create(this.idlClass));
+ // Set this._record (not this.record) to avoid loop in initRecord()
+ this._record = this.record || this.idl.create(this.idlClass);
+ this._recordId = null; // avoid future confusion
return this.getFieldList();
}
// Modifies the provided FM record in place, replacing JS values
// with IDL-compatible values.
convertDatatypesToIdl(rec: IdlObject) {
- const fields = this.idlDef.fields;
+ const fields = this.idlDef.fields.filter(f => !f.virtual);
+
fields.forEach(field => {
if (field.datatype === 'bool') {
if (rec[field.name]() === true) {
private getFieldList(): Promise<any> {
- this.fields = this.idlDef.fields.filter(f =>
- !f.virtual && !this.hiddenFieldsList.includes(f.name)
- );
+ const fields = this.idlDef.fields.filter(f =>
+ !f.virtual && !this.hiddenFieldsList.includes(f.name));
// Wait for all network calls to complete
return Promise.all(
- this.fields.map(field => this.constructOneField(field)));
+ fields.map(field => this.constructOneField(field))
+
+ ).then(() => {
+
+ if (!this.fieldOrder) {
+ this.fields = fields.sort((a, b) => a.label < b.label ? -1 : 1);
+ return;
+ }
+
+ let newList = [];
+ const ordered = this.fieldOrder.split(/,/);
+
+ ordered.forEach(name => {
+ const f1 = fields.filter(f2 => f2.name === name)[0];
+ if (f1) { newList.push(f1); }
+ });
+
+ // Sort remaining fields by label
+ const remainder = fields.filter(f => !ordered.includes(f.name));
+ remainder.sort((a, b) => a.label < b.label ? -1 : 1);
+ newList = newList.concat(remainder);
+
+ this.fields = newList;
+ });
}
private constructOneField(field: any): Promise<any> {
this.convertDatatypesToIdl(recToSave);
this.pcrud[this.mode]([recToSave]).toPromise().then(
result => {
- this.onSave$.emit(result);
+ this.recordSaved.emit(result);
this.successStr.current().then(msg => this.toast.success(msg));
if (this.isDialog()) { this.record = undefined; this.close(result); }
},
error => {
- this.onError$.emit(error);
+ this.recordError.emit(error);
this.failStr.current().then(msg => this.toast.warning(msg));
if (this.isDialog()) { this.error(error); }
}
);
}
+ remove() {
+ this.confirmDel.open().subscribe(confirmed => {
+ if (!confirmed) { return; }
+ const recToRemove = this.idl.clone(this.record);
+ this.pcrud.remove(recToRemove).toPromise().then(
+ result => {
+ this.recordDeleted.emit(result);
+ this.successStr.current().then(msg => this.toast.success(msg));
+ if (this.isDialog()) { this.close(result); }
+ },
+ error => {
+ this.recordError.emit(error);
+ this.failStr.current().then(msg => this.toast.warning(msg));
+ if (this.isDialog()) { this.error(error); }
+ }
+ );
+ });
+ }
+
cancel() {
- this.onCancel$.emit(this.record);
+ this.recordCanceled.emit(this.record);
this.record = undefined;
this.close();
}
--- /dev/null
+import {NgModule} from '@angular/core';
+import {EgCommonModule} from '@eg/common.module';
+import {CommonWidgetsModule} from '@eg/share/common-widgets.module';
+import {StringModule} from '@eg/share/string/string.module';
+import {TranslateModule} from '@eg/share/translate/translate.module';
+import {FmRecordEditorComponent} from './fm-editor.component';
+import {FmRecordEditorActionComponent} from './fm-editor-action.component';
+
+
+@NgModule({
+ declarations: [
+ FmRecordEditorComponent,
+ FmRecordEditorActionComponent
+ ],
+ imports: [
+ EgCommonModule,
+ StringModule,
+ TranslateModule,
+ CommonWidgetsModule
+ ],
+ exports: [
+ FmRecordEditorComponent,
+ FmRecordEditorActionComponent
+ ],
+ providers: [
+ ]
+})
+
+export class FmRecordEditorModule { }
+
--- /dev/null
+import {NgModule} from '@angular/core';
+import {EgCommonModule} from '@eg/common.module';
+import {EgCoreModule} from '@eg/core/core.module';
+import {CommonWidgetsModule} from '@eg/share/common-widgets.module';
+import {OrgFamilySelectComponent} from './org-family-select.component';
+import {ReactiveFormsModule} from '@angular/forms';
+
+@NgModule({
+ declarations: [
+ OrgFamilySelectComponent
+ ],
+ imports: [
+ EgCommonModule,
+ EgCoreModule,
+ CommonWidgetsModule,
+ ReactiveFormsModule
+ ],
+ exports: [
+ OrgFamilySelectComponent
+ ],
+ providers: [
+ ]
+})
+
+export class OrgFamilySelectModule { }
+
--- /dev/null
+import {NgModule} from '@angular/core';
+import {EgCoreModule} from '@eg/core/core.module';
+import {StringComponent} from '@eg/share/string/string.component';
+import {StringService} from '@eg/share/string/string.service';
+
+
+@NgModule({
+ declarations: [
+ StringComponent
+ ],
+ imports: [
+ EgCoreModule
+ ],
+ exports: [
+ StringComponent
+ ],
+ providers: [
+ StringService
+ ]
+})
+
+export class StringModule { }
+
--- /dev/null
+<ng-template #dialogContent>
+ <div class="modal-header bg-info">
+ <h4 class="modal-title" i18n>
+ {{idlClassDef.label}}
+ </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 form-common form-validated" *ngIf="idlObj">
+ <div class="form-group row">
+ <label class="col-lg-4 text-right font-weight-bold"
+ i18n>Field Name</label>
+ <input
+ type="text"
+ [disabled]="true"
+ class="form-control col-lg-7"
+ value="{{idlClassDef.field_map[field].label}}">
+ </div>
+ <div class="form-group row">
+ <label class="col-lg-4 text-right font-weight-bold"
+ i18n>Current Value</label>
+ <input
+ type="text"
+ [disabled]="true"
+ class="form-control col-lg-7"
+ value="{{idlObj[field]()}}">
+ </div>
+ <div class="form-group row">
+ <label class="col-lg-4 text-right font-weight-bold"
+ i18n>Select Locale</label>
+ <select class="form-control col-lg-7"
+ (change)="localeChanged($event)"
+ [(ngModel)]="selectedLocale">
+ <option value="{{locale.code()}}" *ngFor="let locale of locales">
+ {{locale.name()}}
+ </option>
+ </select>
+ </div>
+ <div class="form-group row">
+ <label class="col-lg-4 text-right font-weight-bold" i18n>Translation</label>
+ <input
+ id='translation-input'
+ type="text"
+ class="form-control col-lg-7"
+ required
+ i18n-placeholder
+ (keyup.enter)="translate()"
+ placeholder="Translation..."
+ [(ngModel)]="translatedValue"/>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button *ngIf="prevString" (click)="prevString()"
+ class="btn btn-info" i18n>Prev String</button>
+ <button *ngIf="nextString" (click)="nextString()"
+ class="btn btn-info mr-3" i18n>Next String</button>
+ <button (click)="translate()" class="btn btn-info" i18n>Apply</button>
+ <button (click)="close()" class="btn btn-warning ml-2" i18n>Cancel</button>
+ </div>
+</ng-template>
--- /dev/null
+import {Component, OnInit, Input, Renderer2} from '@angular/core';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {LocaleService} from '@eg/core/locale.service';
+import {AuthService} from '@eg/core/auth.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
+
+@Component({
+ selector: 'eg-translate',
+ templateUrl: 'translate.component.html'
+})
+
+export class TranslateComponent
+ extends DialogComponent implements OnInit {
+
+ idlClassDef: any;
+ locales: IdlObject[];
+ selectedLocale: string;
+ translatedValue: string;
+ existingTranslation: IdlObject;
+
+ // These actions should update the idlObject and/or fieldName values,
+ // forcing the dialog to load a new string to translate. When set,
+ // applying a translation in the dialog will leave the dialog window open
+ // so the next/prev buttons can be used to fetch the next string.
+ nextString: () => void;
+ prevString: () => void;
+
+ idlObj: IdlObject;
+ @Input() set idlObject(o: IdlObject) {
+ if (o) {
+ this.idlObj = o;
+ this.idlClassDef = this.idl.classes[o.classname];
+ this.fetchTranslation();
+ }
+ }
+
+ field: string;
+ @Input() set fieldName(n: string) {
+ this.field = n;
+ }
+
+ constructor(
+ private modal: NgbModal, // required for passing to parent
+ private renderer: Renderer2,
+ private idl: IdlService,
+ private toast: ToastService,
+ private locale: LocaleService,
+ private pcrud: PcrudService,
+ private auth: AuthService) {
+ super(modal);
+ }
+
+ ngOnInit() {
+ // Default to the login locale
+ this.selectedLocale = this.locale.currentLocaleCode();
+ this.locales = [];
+ this.locale.supportedLocales().subscribe(l => this.locales.push(l));
+
+ this.onOpen$.subscribe(() => {
+ const elm = this.renderer.selectRootElement('#translation-input');
+ if (elm) {
+ elm.focus();
+ elm.select();
+ }
+ });
+ }
+
+ localeChanged(code: string) {
+ this.fetchTranslation();
+ }
+
+ fetchTranslation() {
+ const exist = this.existingTranslation;
+
+ if (exist
+ && exist.fq_field() === this.fqField()
+ && exist.identity_value() === this.identValue()) {
+ // Already have the current translation object.
+ return;
+ }
+
+ this.translatedValue = '';
+ this.existingTranslation = null;
+
+ this.pcrud.search('i18n', {
+ translation: this.selectedLocale,
+ fq_field : this.fqField(),
+ identity_value: this.identValue()
+ }).subscribe(tr => {
+ this.existingTranslation = tr;
+ this.translatedValue = tr.string();
+ console.debug('found existing translation ', tr);
+ });
+ }
+
+ fqField(): string {
+ return this.idlClassDef.classname + '.' + this.field;
+ }
+
+ identValue(): string {
+ return this.idlObj[this.idlClassDef.pkey || 'id']();
+ }
+
+ translate() {
+ if (!this.translatedValue) { return; }
+
+ let entry;
+
+ if (this.existingTranslation) {
+ entry = this.existingTranslation;
+ entry.string(this.translatedValue);
+
+ this.pcrud.update(entry).toPromise().then(
+ ok => {
+ if (!this.nextString) {
+ this.close(this.translatedValue);
+ }
+ },
+ err => console.error(err)
+ );
+
+ return;
+ }
+
+ entry = this.idl.create('i18n');
+ entry.fq_field(this.fqField());
+ entry.identity_value(this.identValue());
+ entry.translation(this.selectedLocale);
+ entry.string(this.translatedValue);
+
+ this.pcrud.create(entry).toPromise().then(
+ ok => {
+ if (!this.nextString) {
+ this.close(this.translatedValue);
+ }
+ },
+ err => console.error('Translation creation failed')
+ );
+ }
+}
+
+
--- /dev/null
+import {NgModule} from '@angular/core';
+import {EgCommonModule} from '@eg/common.module';
+import {EgCoreModule} from '@eg/core/core.module';
+import {TranslateComponent} from './translate.component';
+
+
+@NgModule({
+ declarations: [
+ TranslateComponent
+ ],
+ imports: [
+ EgCommonModule,
+ EgCoreModule
+ ],
+ exports: [
+ TranslateComponent
+ ],
+ providers: [
+ ]
+})
+
+export class TranslateModule { }
+
import {NgModule} from '@angular/core';
import {StaffCommonModule} from '@eg/staff/common.module';
import {LinkTableComponent, LinkTableLinkComponent} from '@eg/staff/share/link-table/link-table.component';
+import {TranslateModule} from '@eg/share/translate/translate.module';
+import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module';
+import {AdminPageModule} from '@eg/staff/share/admin-page/admin-page.module';
import {BasicAdminPageComponent} from '@eg/staff/admin/basic-admin-page.component';
@NgModule({
BasicAdminPageComponent
],
imports: [
- StaffCommonModule
+ StaffCommonModule,
+ TranslateModule,
+ FmRecordEditorModule,
+ AdminPageModule
],
exports: [
StaffCommonModule,
+ TranslateModule,
+ FmRecordEditorModule,
+ AdminPageModule,
LinkTableComponent,
LinkTableLinkComponent,
BasicAdminPageComponent
import {NoTimezoneSetComponent} from './no-timezone-set.component';
import {PatronService} from '@eg/staff/share/patron.service';
import {BookingResourceBarcodeValidatorDirective} from './booking_resource_validator.directive';
+import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module';
+import {OrgFamilySelectModule} from '@eg/share/org-family-select/org-family-select.module';
@NgModule({
StaffCommonModule,
BookingRoutingModule,
ReactiveFormsModule,
+ FmRecordEditorModule,
+ OrgFamilySelectModule
],
providers: [PatronService],
declarations: [
openReservationViewer = (id: number): void => {
this.viewReservation.mode = 'view';
- this.viewReservation.recId = id;
+ this.viewReservation.recordId = id;
this.viewReservation.open({ size: 'lg' });
}
}
showEditDialog(idlThing: IdlObject) {
- this.editDialog.recId = idlThing.id();
+ this.editDialog.recordId = idlThing.id();
this.editDialog.timezone = idlThing['timezone'];
return new Promise((resolve, reject) => {
this.editDialog.open({size: 'lg'}).subscribe(
this.grid.onRowActivate.subscribe(
(matchSet: IdlObject) => {
this.editDialog.mode = 'update';
- this.editDialog.recId = matchSet.id();
+ this.editDialog.recordId = matchSet.id();
this.editDialog.open({size: 'lg'})
.subscribe(() => this.grid.reload());
}
import {NgModule} from '@angular/core';
+import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module';
import {StaffCommonModule} from '@eg/staff/common.module';
import {CatalogCommonModule} from '@eg/share/catalog/catalog-common.module';
import {HttpClientModule} from '@angular/common/http';
import {TreeModule} from '@eg/share/tree/tree.module';
+import {AdminPageModule} from '@eg/staff/share/admin-page/admin-page.module';
import {VandelayRoutingModule} from './routing.module';
import {VandelayService} from './vandelay.service';
import {VandelayComponent} from './vandelay.component';
imports: [
TreeModule,
StaffCommonModule,
+ FmRecordEditorModule,
+ AdminPageModule,
CatalogCommonModule,
VandelayRoutingModule,
HttpClientModule,
import {NgModule} from '@angular/core';
+import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module';
import {StaffCommonModule} from '@eg/staff/common.module';
import {CatalogCommonModule} from '@eg/share/catalog/catalog-common.module';
import {CatalogRoutingModule} from './routing.module';
],
imports: [
StaffCommonModule,
+ FmRecordEditorModule,
CatalogCommonModule,
CatalogRoutingModule,
HoldsModule,
this.partsGrid.onRowActivate.subscribe(
(part: IdlObject) => {
this.editDialog.mode = 'update';
- this.editDialog.recId = part.id();
+ this.editDialog.recordId = part.id();
this.editDialog.open()
.subscribe(ok => this.partsGrid.reload());
}
this.createNew = () => {
const part = this.idl.create('bmp');
- part.record(this.recId);
+ part.record(this.recordId);
this.editDialog.record = part;
this.editDialog.mode = 'create';
import {AudioService} from '@eg/share/util/audio.service';
import {GridModule} from '@eg/share/grid/grid.module';
import {StaffBannerComponent} from './share/staff-banner.component';
-import {OrgFamilySelectComponent} from '@eg/share/org-family-select/org-family-select.component';
import {AccessKeyDirective} from '@eg/share/accesskey/accesskey.directive';
import {AccessKeyService} from '@eg/share/accesskey/accesskey.service';
import {AccessKeyInfoComponent} from '@eg/share/accesskey/accesskey-info.component';
import {OpChangeComponent} from '@eg/staff/share/op-change/op-change.component';
import {TitleComponent} from '@eg/share/title/title.component';
-import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
import {BucketDialogComponent} from '@eg/staff/share/buckets/bucket-dialog.component';
import {BibSummaryComponent} from '@eg/staff/share/bib-summary/bib-summary.component';
-import {TranslateComponent} from '@eg/staff/share/translate/translate.component';
-import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.component';
import {EgHelpPopoverComponent} from '@eg/share/eg-help-popover/eg-help-popover.component';
import {DatetimeValidatorDirective} from '@eg/share/validators/datetime_validator.directive';
import {MultiSelectComponent} from '@eg/share/multi-select/multi-select.component';
@NgModule({
declarations: [
StaffBannerComponent,
- OrgFamilySelectComponent,
AccessKeyDirective,
AccessKeyInfoComponent,
TitleComponent,
OpChangeComponent,
- FmRecordEditorComponent,
BucketDialogComponent,
BibSummaryComponent,
- TranslateComponent,
- AdminPageComponent,
EgHelpPopoverComponent,
DatetimeValidatorDirective,
MultiSelectComponent,
CommonWidgetsModule,
GridModule,
StaffBannerComponent,
- OrgFamilySelectComponent,
AccessKeyDirective,
AccessKeyInfoComponent,
TitleComponent,
OpChangeComponent,
- FmRecordEditorComponent,
BucketDialogComponent,
BibSummaryComponent,
- TranslateComponent,
- AdminPageComponent,
EgHelpPopoverComponent,
DatetimeValidatorDirective,
MultiSelectComponent,
<!-- note: fieldOptions would be best defined in the .ts file, but
want to demostrate it can be set in the template as well -->
<eg-fm-record-editor #fmRecordEditor
- idlClass="cmrcfld" mode="create"
- [fieldOptions]="{marc_record_type:{customValues:[{id:'biblio'},{id:'serial'},{id:'authority'}]},description:{customTemplate:{template:descriptionTemplate,context:{'hello':'goodbye'}}}}"
- recordId="1" orgDefaultAllowed="owner">
+ idlClass="cmrcfld" mode="create" hiddenFields="id"
+ fieldOrder="owner,name,description,marc_format,marc_record_type,tag"
+ [fieldOptions]="{marc_record_type:{customValues:[{id:'biblio'},{id:'serial'},{id:'authority'}]},description:{customTemplate:{template:descriptionTemplate,context:{'hello':'goodbye'}}}}"
+ recordId="1" orgDefaultAllowed="owner">
</eg-fm-record-editor>
<button class="btn btn-dark" (click)="openEditor()">
Fm Record Editor
showEditDialog(idlThing: IdlObject): Promise<any> {
this.editDialog.mode = 'update';
- this.editDialog.recId = idlThing['id']();
+ this.editDialog.recordId = idlThing['id']();
return new Promise((resolve, reject) => {
this.editDialog.open({size: 'lg'}).subscribe(
ok => {
import {NgModule} from '@angular/core';
+import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module';
import {StaffCommonModule} from '@eg/staff/common.module';
+import {TranslateModule} from '@eg/share/translate/translate.module';
import {SandboxRoutingModule} from './routing.module';
import {SandboxComponent} from './sandbox.component';
import {ReactiveFormsModule} from '@angular/forms';
import {SampleDataService} from '@eg/share/util/sample-data.service';
+import {OrgFamilySelectModule} from '@eg/share/org-family-select/org-family-select.module';
@NgModule({
declarations: [
],
imports: [
StaffCommonModule,
+ TranslateModule,
+ FmRecordEditorModule,
+ OrgFamilySelectModule,
SandboxRoutingModule,
ReactiveFormsModule
],
import {IdlService, IdlObject} from '@eg/core/idl.service';
import {GridDataSource} from '@eg/share/grid/grid';
import {GridComponent} from '@eg/share/grid/grid.component';
-import {TranslateComponent} from '@eg/staff/share/translate/translate.component';
+import {TranslateComponent} from '@eg/share/translate/translate.component';
import {ToastService} from '@eg/share/toast/toast.service';
import {Pager} from '@eg/share/util/pager';
import {PcrudService} from '@eg/core/pcrud.service';
showEditDialog(idlThing: IdlObject): Promise<any> {
this.editDialog.mode = 'update';
- this.editDialog.recId = idlThing[this.pkeyField]();
+ this.editDialog.recordId = idlThing[this.pkeyField]();
return new Promise((resolve, reject) => {
this.editDialog.open({size: this.dialogSize}).subscribe(
result => {
this.editDialog.mode = 'create';
// We reuse the same editor for all actions. Be sure
// create action does not try to modify an existing record.
- this.editDialog.recId = null;
+ this.editDialog.recordId = null;
this.editDialog.record = null;
this.editDialog.open({size: this.dialogSize}).subscribe(
ok => {
--- /dev/null
+import {NgModule} from '@angular/core';
+import {EgCommonModule} from '@eg/common.module';
+import {EgCoreModule} from '@eg/core/core.module';
+import {GridModule} from '@eg/share/grid/grid.module';
+import {StringModule} from '@eg/share/string/string.module';
+import {TranslateModule} from '@eg/share/translate/translate.module';
+import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module';
+import {AdminPageComponent} from './admin-page.component';
+import {OrgFamilySelectModule} from '@eg/share/org-family-select/org-family-select.module';
+
+
+@NgModule({
+ declarations: [
+ AdminPageComponent
+ ],
+ imports: [
+ EgCommonModule,
+ EgCoreModule,
+ StringModule,
+ OrgFamilySelectModule,
+ TranslateModule,
+ FmRecordEditorModule,
+ GridModule
+ ],
+ exports: [
+ OrgFamilySelectModule,
+ AdminPageComponent
+ ],
+ providers: [
+ ]
+})
+
+export class AdminPageModule { }
+
+++ /dev/null
-<ng-template #dialogContent>
- <div class="modal-header bg-info">
- <h4 class="modal-title" i18n>
- {{idlClassDef.label}}
- </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 form-common form-validated" *ngIf="idlObj">
- <div class="form-group row">
- <label class="col-lg-4 text-right font-weight-bold"
- i18n>Field Name</label>
- <input
- type="text"
- [disabled]="true"
- class="form-control col-lg-7"
- value="{{idlClassDef.field_map[field].label}}">
- </div>
- <div class="form-group row">
- <label class="col-lg-4 text-right font-weight-bold"
- i18n>Current Value</label>
- <input
- type="text"
- [disabled]="true"
- class="form-control col-lg-7"
- value="{{idlObj[field]()}}">
- </div>
- <div class="form-group row">
- <label class="col-lg-4 text-right font-weight-bold"
- i18n>Select Locale</label>
- <select class="form-control col-lg-7"
- (change)="localeChanged($event)"
- [(ngModel)]="selectedLocale">
- <option value="{{locale.code()}}" *ngFor="let locale of locales">
- {{locale.name()}}
- </option>
- </select>
- </div>
- <div class="form-group row">
- <label class="col-lg-4 text-right font-weight-bold" i18n>Translation</label>
- <input
- id='translation-input'
- type="text"
- class="form-control col-lg-7"
- required
- i18n-placeholder
- (keyup.enter)="translate()"
- placeholder="Translation..."
- [(ngModel)]="translatedValue"/>
- </div>
- </div>
- <div class="modal-footer">
- <button *ngIf="prevString" (click)="prevString()"
- class="btn btn-info" i18n>Prev String</button>
- <button *ngIf="nextString" (click)="nextString()"
- class="btn btn-info mr-3" i18n>Next String</button>
- <button (click)="translate()" class="btn btn-info" i18n>Apply</button>
- <button (click)="close()" class="btn btn-warning ml-2" i18n>Cancel</button>
- </div>
-</ng-template>
+++ /dev/null
-import {Component, OnInit, Input, Renderer2} from '@angular/core';
-import {IdlService, IdlObject} from '@eg/core/idl.service';
-import {ToastService} from '@eg/share/toast/toast.service';
-import {LocaleService} from '@eg/core/locale.service';
-import {AuthService} from '@eg/core/auth.service';
-import {PcrudService} from '@eg/core/pcrud.service';
-import {DialogComponent} from '@eg/share/dialog/dialog.component';
-import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
-
-@Component({
- selector: 'eg-translate',
- templateUrl: 'translate.component.html'
-})
-
-export class TranslateComponent
- extends DialogComponent implements OnInit {
-
- idlClassDef: any;
- locales: IdlObject[];
- selectedLocale: string;
- translatedValue: string;
- existingTranslation: IdlObject;
-
- // These actions should update the idlObject and/or fieldName values,
- // forcing the dialog to load a new string to translate. When set,
- // applying a translation in the dialog will leave the dialog window open
- // so the next/prev buttons can be used to fetch the next string.
- nextString: () => void;
- prevString: () => void;
-
- idlObj: IdlObject;
- @Input() set idlObject(o: IdlObject) {
- if (o) {
- this.idlObj = o;
- this.idlClassDef = this.idl.classes[o.classname];
- this.fetchTranslation();
- }
- }
-
- field: string;
- @Input() set fieldName(n: string) {
- this.field = n;
- }
-
- constructor(
- private modal: NgbModal, // required for passing to parent
- private renderer: Renderer2,
- private idl: IdlService,
- private toast: ToastService,
- private locale: LocaleService,
- private pcrud: PcrudService,
- private auth: AuthService) {
- super(modal);
- }
-
- ngOnInit() {
- // Default to the login locale
- this.selectedLocale = this.locale.currentLocaleCode();
- this.locales = [];
- this.locale.supportedLocales().subscribe(l => this.locales.push(l));
-
- this.onOpen$.subscribe(() => {
- const elm = this.renderer.selectRootElement('#translation-input');
- if (elm) {
- elm.focus();
- elm.select();
- }
- });
- }
-
- localeChanged(code: string) {
- this.fetchTranslation();
- }
-
- fetchTranslation() {
- const exist = this.existingTranslation;
-
- if (exist
- && exist.fq_field() === this.fqField()
- && exist.identity_value() === this.identValue()) {
- // Already have the current translation object.
- return;
- }
-
- this.translatedValue = '';
- this.existingTranslation = null;
-
- this.pcrud.search('i18n', {
- translation: this.selectedLocale,
- fq_field : this.fqField(),
- identity_value: this.identValue()
- }).subscribe(tr => {
- this.existingTranslation = tr;
- this.translatedValue = tr.string();
- console.debug('found existing translation ', tr);
- });
- }
-
- fqField(): string {
- return this.idlClassDef.classname + '.' + this.field;
- }
-
- identValue(): string {
- return this.idlObj[this.idlClassDef.pkey || 'id']();
- }
-
- translate() {
- if (!this.translatedValue) { return; }
-
- let entry;
-
- if (this.existingTranslation) {
- entry = this.existingTranslation;
- entry.string(this.translatedValue);
-
- this.pcrud.update(entry).toPromise().then(
- ok => {
- if (!this.nextString) {
- this.close(this.translatedValue);
- }
- },
- err => console.error(err)
- );
-
- return;
- }
-
- entry = this.idl.create('i18n');
- entry.fq_field(this.fqField());
- entry.identity_value(this.identValue());
- entry.translation(this.selectedLocale);
- entry.string(this.translatedValue);
-
- this.pcrud.create(entry).toPromise().then(
- ok => {
- if (!this.nextString) {
- this.close(this.translatedValue);
- }
- },
- err => console.error('Translation creation failed')
- );
- }
-}
-
-