From: Bill Erickson Date: Mon, 16 Apr 2018 19:10:39 +0000 (+0000) Subject: LP#1626157 fm editor xport experiment X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=15f3e37c1470ca8df371e6a4b8172a4a6eda55e1;p=working%2FEvergreen.git LP#1626157 fm editor xport experiment Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/eg2/src/app/share/accesskey/accesskey-info.component.html b/Open-ILS/src/eg2/src/app/share/accesskey/accesskey-info.component.html index cbb00887ce..82ed72a4b0 100644 --- a/Open-ILS/src/eg2/src/app/share/accesskey/accesskey-info.component.html +++ b/Open-ILS/src/eg2/src/app/share/accesskey/accesskey-info.component.html @@ -21,6 +21,6 @@ diff --git a/Open-ILS/src/eg2/src/app/share/dialog/dialog.component.ts b/Open-ILS/src/eg2/src/app/share/dialog/dialog.component.ts index 4da4f0a661..0b2cbbcbbe 100644 --- a/Open-ILS/src/eg2/src/app/share/dialog/dialog.component.ts +++ b/Open-ILS/src/eg2/src/app/share/dialog/dialog.component.ts @@ -1,5 +1,5 @@ import {Component, Input, ViewChild, TemplateRef} from '@angular/core'; -import {NgbModal, NgbModalRef} from '@ng-bootstrap/ng-bootstrap'; +import {NgbModal, NgbModalRef, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap'; /** * Dialog base class. Handles the ngbModal logic. @@ -25,14 +25,14 @@ export class EgDialogComponent { constructor(private modalService: NgbModal) {} - open(): Promise { + open(options?: NgbModalOptions): Promise { if (this.modalRef !== null) { console.warn('Dismissing existing dialog'); this.dismiss(); } - this.modalRef = this.modalService.open(this.dialogContent); + this.modalRef = this.modalService.open(this.dialogContent, options); return new Promise( (resolve, reject) => { this.modalRef.result.then( diff --git a/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.html b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.html new file mode 100644 index 0000000000..c49da97493 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.html @@ -0,0 +1,44 @@ + + + + + diff --git a/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts new file mode 100644 index 0000000000..b2fbe6d80d --- /dev/null +++ b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts @@ -0,0 +1,174 @@ +import {Component, OnInit, Input, Output, ViewChild, EventEmitter} from '@angular/core'; +import {EgStoreService} from '@eg/core/store.service'; +import {EgOrgService} from '@eg/core/org.service'; +import {EgIdlService, EgIdlObject} from '@eg/core/idl.service'; +import {EgPcrudService} from '@eg/core/pcrud.service'; +import {EgDialogComponent} from '@eg/share/dialog/dialog.component'; +import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'fm-record-editor', + templateUrl: './fm-editor.component.html' +}) +export class FmRecordEditorComponent + extends EgDialogComponent implements OnInit { + + // IDL class hint (e.g. "aou") + @Input() idlClass: string; + + // mode: 'create' for creating a new record, + // 'update' for editing an existing record + @Input() mode: string; + + // record ID to update. Not all IDs are numbers. + @Input() recordId: string; + + // TODO + // customFieldTemplates + + // list of fields that should not be displayed + @Input() hiddenFields: string[] = []; + + // list of fields that should always be read-only + @Input() readonlyFields: string[] = []; + + // list of required fields; this supplements what the IDL considers + // required + @Input() requiredFields: string[] = []; + + // list of org_unit fields where the selector should default to the + // workstation OU + @Input() orgDefaultAllowed: string[] = []; + + // hash, keyed by field name, of functions to invoke to check + // whether a field is required. Each callback is passed the field + // name and the record and should return a boolean value. This + // supports cases where whether a field is required or not depends + // on the current value of another field. + @Input() isRequiredOverride: + {[field: string] : (field: string, record: EgIdlObject) => boolean}; + + // IDL record display label. Defaults to the IDL label. + @Input() recordLabel: string; + + // Emit the modified object when the save action completes. + @Output() onSave$ = new EventEmitter(); + + // Emit the original object when the save action is canceled. + @Output() onCancel$ = new EventEmitter(); + + // Emit an error message when the save action fails. + @Output() onError$ = new EventEmitter(); + + // IDL info for the the selected IDL class + idlDef: any; + + // IDL record we are editing + record: EgIdlObject; + + // Can we edit the primary key? + pkeyIsEditable: boolean = false; + + // List of IDL field definitions. This is a subset of the full + // list of fields on the IDL, since some are hidden, virtual, etc. + fields: any[]; + + constructor( + private modal: NgbModal, // required for passing to parent + private idl: EgIdlService, + private store: EgStoreService, + private org: EgOrgService, + private pcrud: EgPcrudService + ) { + super(modal) + } + + ngOnInit() { + this.idlDef = this.idl.classes[this.idlClass]; + this.recordLabel = this.idlDef.label; + this.initRecord(); + } + + private initRecord(): Promise { + + if (this.mode == 'update') { + return this.pcrud.retrieve(this.idlClass, this.recordId) + .toPromise().then(rec => { + this.record = rec; + this.convertDatatypesToJs(); + this.fields = this.getFieldList(); + }); + } + + // create a new record from scratch + this.pkeyIsEditable = !('pkey_sequence' in this.idlDef); + this.record = this.idl.create(this.idlClass); + this.fields = this.getFieldList(); + return Promise.resolve(); + } + + // Modifies the FM record in place, replacing IDL-compatible values + // with native JS values. + private convertDatatypesToJs() { + this.idlDef.fields.forEach(field => { + if (field.datatype == 'bool') { + if (this.record[field.name]() == 't') { + this.record[field.name](true); + } else if (this.record[field.name]() == 'f') { + this.record[field.name](false); + } + } + }); + } + + private flattenLinkedValues(cls: string, list: EgIdlObject[]): any[] { + let results: any[] = []; + let idField: string = this.idlDef.pkey; + let selector: string = + this.idlDef.field_map[idField].selector || idField; + + return list.map(item => { + return {id: item[idField](), name: item[selector]()} + }); + } + + private getFieldList(): any[] { + + let fields = this.idlDef.fields.filter(f => + f.virtual != 'true' && + !this.hiddenFields.includes(f.name) + ); + + fields.forEach(field => { + field.readOnly = this.readonlyFields.includes(field.name); + + if (this.isRequiredOverride && + field.name in this.isRequiredOverride) { + field.isRequired = () => { + return this.isRequiredOverride[field.name](field.name, this.record); + } + } else { + field.isRequired = () => { + return field.required || + this.requiredFields.includes(field.name); + } + } + + // TODO + }); + + console.debug(fields); + return fields; + } + + save() { + // save changes here + this.close(/* data for caller */); + } + + cancel() { + this.dismiss(/* data for caller */); + } +} + + diff --git a/Open-ILS/src/eg2/src/app/share/org-select.component.html b/Open-ILS/src/eg2/src/app/share/org-select.component.html index e628ab2cee..2a4bd3a0c2 100644 --- a/Open-ILS/src/eg2/src/app/share/org-select.component.html +++ b/Open-ILS/src/eg2/src/app/share/org-select.component.html @@ -5,7 +5,7 @@ (); click$ = new Subject(); @ViewChild('instance') instance: NgbTypeahead; @@ -86,7 +85,7 @@ export class EgOrgSelectComponent implements OnInit { filter = (text$: Observable): Observable => { return text$ - .debounceTime(100) + .debounceTime(200) .distinctUntilChanged() .merge(this.click$.filter(() => !this.instance.isPopupOpen())) .map(term => { diff --git a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.html b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.html index cf5d89b751..32a14491ab 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.html +++ b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.html @@ -9,6 +9,11 @@ dialogBody='Workstation "{{newName}}" already exists. Use it anyway?'> + + + + +
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.module.ts index 064b24dd45..9692435d2e 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.module.ts @@ -3,9 +3,13 @@ import {EgStaffCommonModule} from '@eg/staff/common.module'; import {WorkstationsRoutingModule} from './routing.module'; import {WorkstationsComponent} from './workstations.component'; +// XXX testing +import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component'; + @NgModule({ declarations: [ WorkstationsComponent, + FmRecordEditorComponent // XXX ], imports: [ EgStaffCommonModule, diff --git a/Open-ILS/src/eg2/src/styles.css b/Open-ILS/src/eg2/src/styles.css index 9f7b83a8b2..835d80d07b 100644 --- a/Open-ILS/src/eg2/src/styles.css +++ b/Open-ILS/src/eg2/src/styles.css @@ -40,7 +40,7 @@ h5 {font-size: .95rem} .flex-5 {flex: 5} -/* usefulf for mat-icon buttons without any background or borders */ +/* usefuf for mat-icon buttons without any background or borders */ .material-icon-button { /* Transparent background */ border: none; @@ -67,3 +67,14 @@ h5 {font-size: .95rem} padding: .25rem; } +@media all and (min-width: 800px) { + /* scrollable typeahead menus for full-size screens */ + ngb-typeahead-window { + height: auto; + max-height: 200px; + overflow-x: hidden; + } +} + + +