--- /dev/null
+<eg-title i18n-prefix prefix="Field Documentation"></eg-title>
+<eg-staff-banner bannerText="Field Documentation" i18n-bannerText></eg-staff-banner>
+
+<div class="row mt-3">
+ <div class="col-md-3">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <div class="input-group-text" i18n>Class</div>
+ <!-- IDL Selector -->
+ <eg-combobox [allowFreeText]="true"
+ [entries]="idlEntries" [(ngModel)]="selectedClass"
+ (ngModelChange)="setGrid()">
+ </eg-combobox>
+ </div>
+ </div>
+ </div>
+
+</div>
+
+<div class='w-11 mt-3'>
+ <eg-grid #fieldDocGrid [dataSource]="gridDataSource"
+ persistKey="admin.config.idl_field_doc" idlClass="fdoc">
+ <eg-grid-toolbar-button
+ label="New Field Documentation" i18n-label (onClick)="createNew()">
+ </eg-grid-toolbar-button>
+ <eg-grid-toolbar-action label="Edit Selected" i18n-label (onClick)="editSelected($event)">
+ </eg-grid-toolbar-action>
+ </eg-grid>
+</div>
+
+<eg-fm-record-editor #editDialog hiddenFields="id" idlClass="fdoc"
+ requiredFields="fm_class,field,owner,string" [(fieldOptions)]="fieldOptions">
+</eg-fm-record-editor>
+
+<ng-template #fieldClassSelector let-fieldentries="fieldentries" let-selected="selectedEntry">
+ <eg-combobox [allowFreeText]="true" [(ngModel)]="selected" required="true"
+ [entries]="fieldentries" (onChange)="setClass($event)">
+ </eg-combobox>
+</ng-template>
+
+<ng-template #fieldSelector
+ let-entries="fields" let-selected="selectedEntry">
+ <eg-combobox [allowFreeText]="true" required="true"
+ [entries]="fields" [(ngModel)]="selected"
+ (ngModelChange)="setField($event)">
+ </eg-combobox>
+</ng-template>
+
+<eg-string #updateSuccessString text="Updated succeeded!" i18n-text></eg-string>
+<eg-string #updateFailedString text="Updated failed." i18n-text></eg-string>
+<eg-string #createSuccessString text="New Field Documentation Created!" i18n-text></eg-string>
+<eg-string #createFailedString text="Creation of new Field Documentation failed." i18n-text></eg-string>
\ No newline at end of file
--- /dev/null
+import {Component, OnInit, Input, ViewChild, ViewEncapsulation} from'@angular/core';
+import {Router} from '@angular/router';
+import {Observable, Observer, of} from 'rxjs';
+import {map} from 'rxjs/operators';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {Pager} from '@eg/share/util/pager';
+import {AuthService} from '@eg/core/auth.service';
+import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+
+@Component({
+ templateUrl: './field-documentation.component.html'
+})
+
+export class FieldDocumentationComponent implements OnInit {
+
+ idlEntries: any[] = [];
+ fieldOptions: any = {};
+ @Input() selectedClass: any;
+ @Input() fields: [] = [];
+ gridDataSource: GridDataSource;
+ @ViewChild('fieldClassSelector', {static: true}) fieldClassSelector: any;
+ @ViewChild('fieldSelector', {static: true}) fieldSelector: any;
+ @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+ @ViewChild('fieldDocGrid', { static: true }) fieldDocGrid: GridComponent;
+ @ViewChild('updateSuccessString', { static: true }) updateSuccessString: StringComponent;
+ @ViewChild('createSuccessString', { static: false }) createSuccessString: StringComponent;
+ @ViewChild('createFailedString', { static: false }) createFailedString: StringComponent;
+ @ViewChild('updateFailedString', { static: false }) updateFailedString: StringComponent;
+
+ constructor(
+ private auth: AuthService,
+ private idl: IdlService,
+ private pcrud: PcrudService,
+ private toast: ToastService
+ ) {}
+
+ ngOnInit() {
+ this.gridDataSource = new GridDataSource();
+ Object.values(this.idl.classes).forEach(idlClass => {
+ let fields = [];
+ Object.values(idlClass['field_map']).forEach(field => {
+ // We can safely ignore virtual fields...
+ if (!field['virtual']) {
+ fields.push({
+ id: field['name'],
+ label: field['label'] ? field['label'] : field['name']
+ });
+ }
+ });
+ if (idlClass['label']) {
+ this.idlEntries.push({
+ label: idlClass['label'],
+ id: idlClass['name'],
+ fields: fields
+ });
+ }
+ });
+ this.idlEntries.sort((a, b) => {
+ let textA = a.label.toUpperCase();
+ let textB = b.label.toUpperCase();
+ return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
+ });
+ if (this.selectedClass) this.setGrid();
+ this.fieldDocGrid.onRowActivate.subscribe((fieldDoc: IdlObject) => {
+ this.showEditDialog(fieldDoc);
+ });
+
+ this.fieldOptions = {
+ fm_class: {
+ customTemplate: {
+ template:this.fieldClassSelector,
+ context: {
+ fieldentries: this.idlEntries,
+ selectedEntry: this.selectedClass
+ }
+ }
+ },
+ field: {
+ customTemplate: {
+ template: this.fieldSelector,
+ context: {
+ selectedEntry: null
+ }
+ }
+ }
+ }
+ }
+
+ setClass(idlClass, entry?) {
+ if (this.editDialog.record) this.editDialog.record.fm_class(idlClass.id);
+ this.fieldOptions.fm_class.customTemplate.context.selectedEntry = idlClass;
+ this.fields = idlClass.fields;
+
+ if (entry && entry.field()) this.setField(
+ idlClass.fields.find(o => o.id == entry.field()),
+ );
+ }
+
+ setField(entry) {
+ if (this.editDialog.record) this.editDialog.record.field(entry.id);
+ this.fieldOptions.field.customTemplate.context.selectedEntry = entry;
+ }
+
+ setGrid() {
+ this.gridDataSource.data = [];
+ this.setCurrentFieldDoc();
+ }
+
+ setCurrentFieldDoc() {
+ if (this.selectedClass) {
+ this.fields = this.selectedClass.fields;
+ this.pcrud.search('fdoc',
+ {fm_class: this.selectedClass.id}
+ ).subscribe(fdocs => {
+ this.gridDataSource.data.push(fdocs);
+ });
+ }
+ }
+
+ setFieldOptions(field) {
+ field.owner(this.auth.user().ws_ou());
+ this.fieldOptions.fm_class.customTemplate.context.selectedEntry = this.selectedClass;
+ this.fieldOptions.field.customTemplate.context.fields = this.selectedClass ? this.selectedClass.fields : [];
+ this.fieldOptions.field.customTemplate.context.record = field;
+ if (this.selectedClass) {
+ this.setClass(this.selectedClass, field);
+ }
+ }
+
+ showEditDialog(field: IdlObject): Promise<any> {
+ this.editDialog.mode = 'update';
+ this.editDialog.recordId = field.id();
+ this.setFieldOptions(field);
+ return new Promise((resolve, reject) => {
+ this.editDialog.open({size: 'lg'}).subscribe(result => {
+ this.updateSuccessString.current()
+ .then(str => this.toast.success(str));
+ this.setGrid();
+ resolve(result);
+ }, error => {
+ this.updateFailedString.current()
+ .then(str => this.toast.danger(str));
+ });
+ });
+ }
+
+ editSelected(fields: IdlObject[]) {
+ const editOneFieldDoc = (fieldDoc: IdlObject) => {
+ if (!fieldDoc) return;
+
+ this.showEditDialog(fieldDoc).then(
+ () => editOneFieldDoc(fields.shift())
+ );
+ }
+
+ editOneFieldDoc(fields.shift());
+ }
+
+ createNew() {
+ this.editDialog.mode = 'create';
+ this.editDialog.recordId = null;
+ this.editDialog.record = this.idl.create('fdoc');
+ this.setFieldOptions(this.editDialog.record);
+ this.editDialog.open({size: 'lg'}).subscribe(
+ ok => {
+ this.createSuccessString.current()
+ .then(str => this.toast.success(str));
+ this.setGrid();
+ },
+ rejection => {
+ if (!rejection.dismissed) {
+ this.createFailedString.current()
+ .then(str => this.toast.danger(str));
+ }
+ }
+ );
+ }
+}
\ No newline at end of file