From 8b74cb953c1000fd649b30b19e75ad15bdfc8347 Mon Sep 17 00:00:00 2001 From: Mike Risher Date: Thu, 22 Oct 2020 20:19:51 +0000 Subject: [PATCH] LP1843969 Composite Attribute Entry Defs Create a port of the Coded Value Maps UI from Angular JS to Angular. Allow creation and edits of the Composite Attribute Entry Definitions. Signed-off-by: Mike Risher Signed-off-by: Jason Boyer Signed-off-by: Jane Sandberg --- .../coded-value-maps-routing.module.ts | 19 + .../coded-value-maps.component.html | 46 ++ .../coded-value-maps/coded-value-maps.component.ts | 106 +++++ .../coded-value-maps/coded-value-maps.module.ts | 29 ++ .../coded-value-maps/composite-def.component.html | 82 ++++ .../coded-value-maps/composite-def.component.ts | 478 +++++++++++++++++++++ .../coded-value-maps/composite-new.component.html | 48 +++ .../coded-value-maps/composite-new.component.ts | 84 ++++ .../src/app/staff/admin/server/routing.module.ts | 4 + 9 files changed, 896 insertions(+) create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps-routing.module.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.module.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-def.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-def.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-new.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-new.component.ts diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps-routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps-routing.module.ts new file mode 100644 index 0000000000..fe1a58bc4e --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps-routing.module.ts @@ -0,0 +1,19 @@ +import {NgModule} from '@angular/core'; +import {RouterModule, Routes} from '@angular/router'; +import {CodedValueMapsComponent} from './coded-value-maps.component'; +import {CompositeDefComponent} from './composite-def.component'; + +const routes: Routes = [{ + path: '', + component: CodedValueMapsComponent +}, { + path: 'composite_def/:id', + component: CompositeDefComponent +}]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) + +export class CodedValueMapsRoutingModule {} diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.component.html new file mode 100644 index 0000000000..228e5143aa --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.component.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.component.ts new file mode 100644 index 0000000000..f89a79e428 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.component.ts @@ -0,0 +1,106 @@ +import {Pager} from '@eg/share/util/pager'; +import {Component, ViewChild, OnInit} from '@angular/core'; +import {IdlObject} from '@eg/core/idl.service'; +import {GridDataSource} from '@eg/share/grid/grid'; +import {GridComponent} from '@eg/share/grid/grid.component'; +import {ToastService} from '@eg/share/toast/toast.service'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component'; +import {StringComponent} from '@eg/share/string/string.component'; + +@Component({ + templateUrl: './coded-value-maps.component.html' +}) + +export class CodedValueMapsComponent implements OnInit { + + gridDataSource: GridDataSource = new GridDataSource(); + @ViewChild('createString', { static: true }) createString: StringComponent; + @ViewChild('createErrString', { static: true }) createErrString: StringComponent; + @ViewChild('updateSuccessString', { static: true }) updateSuccessString: StringComponent; + @ViewChild('updateFailedString', { static: true }) updateFailedString: StringComponent; + @ViewChild('deleteFailedString', { static: true }) deleteFailedString: StringComponent; + @ViewChild('deleteSuccessString', { static: true }) deleteSuccessString: StringComponent; + + @ViewChild('grid', {static: true}) grid: GridComponent; + @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent; + + constructor( + private pcrud: PcrudService, + private toast: ToastService, + ) { + } + + ngOnInit() { + this.gridDataSource.getRows = (pager: Pager, sort: any[]) => { + return this.pcrud.retrieveAll('ccvm', {order_by: {ccvm: 'id'}}, {fleshSelectors: true}); + }; + this.grid.onRowActivate.subscribe( + (idlThing: IdlObject) => this.showEditDialog(idlThing) + ); + } + + showEditDialog(standingPenalty: IdlObject): Promise { + this.editDialog.mode = 'update'; + this.editDialog.recordId = standingPenalty['id'](); + return new Promise((resolve, reject) => { + this.editDialog.open({size: 'lg'}).subscribe( + result => { + this.updateSuccessString.current() + .then(str => this.toast.success(str)); + this.grid.reload(); + resolve(result); + }, + error => { + this.updateFailedString.current() + .then(str => this.toast.danger(str)); + reject(error); + } + ); + }); + } + + editSelected = (maps: IdlObject[]) => { + const editOneThing = (map: IdlObject) => { + this.showEditDialog(map).then( + () => editOneThing(maps.shift())); + }; + editOneThing(maps.shift()); + } + + deleteSelected = (idlThings: IdlObject[]) => { + idlThings.forEach(idlThing => idlThing.isdeleted(true)); + this.pcrud.autoApply(idlThings).subscribe( + val => { + console.debug('deleted: ' + val); + this.deleteSuccessString.current() + .then(str => this.toast.success(str)); + }, + err => { + this.deleteFailedString.current() + .then(str => this.toast.danger(str)); + }, + () => this.grid.reload() + ); + } + + createNew = () => { + this.editDialog.mode = 'create'; + this.editDialog.recordId = null; + this.editDialog.record = null; + this.editDialog.open({size: 'lg'}).subscribe( + ok => { + this.createString.current() + .then(str => this.toast.success(str)); + this.grid.reload(); + }, + rejection => { + if (!rejection.dismissed) { + this.createErrString.current() + .then(str => this.toast.danger(str)); + } + } + ); + } + + } diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.module.ts new file mode 100644 index 0000000000..36457f18ce --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/coded-value-maps.module.ts @@ -0,0 +1,29 @@ +import {NgModule} from '@angular/core'; +import {StaffCommonModule} from '@eg/staff/common.module'; +import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module'; +import {TreeModule} from '@eg/share/tree/tree.module'; +import {CodedValueMapsComponent} from './coded-value-maps.component'; +import {CompositeDefComponent} from './composite-def.component'; +import {CompositeNewPointComponent} from './composite-new.component'; +import {CodedValueMapsRoutingModule} from './coded-value-maps-routing.module'; + +@NgModule({ + declarations: [ + CodedValueMapsComponent, + CompositeDefComponent, + CompositeNewPointComponent, + ], + imports: [ + StaffCommonModule, + FmRecordEditorModule, + TreeModule, + CodedValueMapsRoutingModule + ], + exports: [ + ], + providers: [ + ] +}) + +export class CodedValueMapsModule { +} diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-def.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-def.component.html new file mode 100644 index 0000000000..84f94185bf --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-def.component.html @@ -0,0 +1,82 @@ + + +
+ +
+ +
Record Attribute:    {{attribute}}
+
Coded Value:    {{code}} / {{value}}
+ +
+

Composite Data Expression

+
+ {{expressionAsString()}} +
+
+

Composite Data Tree

+
+
+
+ + +
+
+ + +

No tree

+
+ +
+
+
+ + +
+ + + +
+
    +
  1. Define a new node using the above fields.
  2. +
  3. Select a boolean node in the tree.
  4. +
  5. Click the "Add..." button to add the new node + as a child of the selected node.
  6. +
+

Note - A NOT boolean node can only have one child: a record attribute, or an + AND / OR boolean node with its own children.

+
+
To start a new tree add a boolean operator + or record attribute and click "New Tree".
+
+ +
+
+ + +
+
+
+ + + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-def.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-def.component.ts new file mode 100644 index 0000000000..d5f5c89d62 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-def.component.ts @@ -0,0 +1,478 @@ +import {Component, ViewChild, OnInit} from '@angular/core'; +import {Router, ActivatedRoute} from '@angular/router'; +import {Tree, TreeNode} from '@eg/share/tree/tree'; +import {IdlService} from '@eg/core/idl.service'; +import {ToastService} from '@eg/share/toast/toast.service'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {CompositeNewPointComponent} from './composite-new.component'; +import {StringComponent} from '@eg/share/string/string.component'; + +@Component({ + templateUrl: './composite-def.component.html' +}) + +export class CompositeDefComponent implements OnInit { + currentId: number; // ccvm id + + // these values displayed at top of page + code: string; + attribute: string; + value: string; + + // data used to build tree + tree: Tree; + treeIndex = 2; // 1 is always root, so start at 2 + idmap: any = {}; + recordAttrDefs: any = {}; + fetchAttrs: any[] = []; + codedValueMaps: any = {}; + + newPointType: string; + @ViewChild('newPoint', { static: true }) newPoint: CompositeNewPointComponent; + + changesMade = false; + noSavedTreeData = false; + + @ViewChild('saveSuccess', { static: true }) saveSuccess: StringComponent; + @ViewChild('saveFail', { static: true }) saveFail: StringComponent; + + constructor( + private pcrud: PcrudService, + private router: Router, + private route: ActivatedRoute, + private idl: IdlService, + private toast: ToastService, + ) { + } + + ngOnInit() { + this.currentId = parseInt(this.route.snapshot.paramMap.get('id'), 10); + this.getRecordAttrDefs(); + } + + getRecordAttrDefs = () => { + this.pcrud.retrieveAll('crad', {order_by: {crad: 'name'}}, {atomic: true}).subscribe(defs => { + defs.forEach((def) => { + this.recordAttrDefs[def.name()] = def; + }); + this.getCodedMapValues(); + }); + } + + getCodedMapValues = () => { + this.pcrud.search('ccvm', {'id': this.currentId}, + {flesh: 1, flesh_fields: {ccvm: ['composite_def', 'ctype']} }).toPromise().then( + res => { + this.code = res.code(); + this.value = res.value(); + this.attribute = res.ctype().label(); + if (res.composite_def()) { + this.buildTreeStart(res.composite_def().definition()); + } else { + this.noSavedTreeData = true; + } + }); + } + + createNodeLabels = () => { + for (const key of Object.keys(this.idmap)) { + const nodeCallerData = this.idmap[key].callerData.point; + if (nodeCallerData.typeId) { + for (const id of Object.keys(this.codedValueMaps)) { + const m = this.codedValueMaps[id]; + if ((m.code() === nodeCallerData.valueId) && + (m.ctype() === nodeCallerData.typeId)) { + nodeCallerData.valueLabel = m.value(); + } + } + this.idmap[key].label = this.buildLabel(nodeCallerData.typeLabel, nodeCallerData.typeId, + nodeCallerData.valueLabel, nodeCallerData.valueId); + } + } + } + + expressionAsString = () => { + if (!this.tree) { return ''; } + + const renderNode = (node: TreeNode): string => { + const lbl = node.label; + if (!node) { return ''; } + if (node.children.length) { + let negative = ''; + let startParen = '( '; + let endParen = ' )'; + if (lbl === 'NOT') { + negative = 'NOT '; + startParen = ''; // parentheses for NOT are redundant + endParen = ''; + } + if (this.tree.findParentNode(node) === null) { // no parentheses for root node + startParen = ''; + endParen = ''; + } + return negative + startParen + node.children.map(renderNode).join( + ' ' + node.label + ' ') + endParen; + } else if ((lbl !== 'NOT') && (lbl !== 'AND') && (lbl !== 'OR')) { + return node.callerData.point.valueLabel; + } else { + return '()'; + } + }; + return renderNode(this.tree.rootNode); + } + + buildTreeStart = (def) => { + if (def) { + const nodeData = JSON.parse(def); + let rootNode; + if (Array.isArray(nodeData)) { + rootNode = this.addBooleanRootNode('OR'); + nodeData.forEach(n => { + this.buildTree(rootNode, n); + }); + } else { + if (nodeData['_not']) { + rootNode = this.addBooleanRootNode('NOT'); + this.buildTree(rootNode, nodeData['_not']); + } else if (nodeData['0']) { + rootNode = this.addBooleanRootNode('AND'); + for (const key of Object.keys(nodeData)) { + this.buildTree(rootNode, nodeData[key]); + } + } else { // root node is record + const newRootValues = { + typeLabel: this.recordAttrDefs[nodeData._attr].label(), + typeId: nodeData['_attr'], + valueLabel: null, + valueId: nodeData['_val'], + }; + rootNode = { + values: newRootValues + }; + rootNode = this.addRecordRootNode(rootNode); + this.fetchAttrs.push({'-and' : {ctype: nodeData['_attr'], code: nodeData['_val']}}); + } + } + if (this.fetchAttrs.length > 0) { + this.pcrud.search('ccvm', {'-or' : this.fetchAttrs}).subscribe( + data => { + this.codedValueMaps[data.id()] = data; + }, + err => { + console.debug(err); + }, + () => { + this.createNodeLabels(); + } + ); + } + } + } + + buildTree = (parentNode, nodeData) => { + let dataIsArray = false; + if (Array.isArray(nodeData)) { dataIsArray = true; } + const point = { + id: null, + expanded: true, + children: [], + parent: parentNode.id, + label: null, + typeLabel: null, + typeId: null, + valueLabel: null, + valueId: null, + }; + if (nodeData[0] || (nodeData['_not']) || dataIsArray) { + this.buildTreeBoolean(nodeData, dataIsArray, point, parentNode); + } else { // not boolean. it's a record + this.buildTreeRecord(nodeData, point, parentNode); + } + } + + buildTreeBoolean = (nodeData: any, dataIsArray: any, point: any, parentNode) => { + if (dataIsArray) { + point.label = 'OR'; + } else if (nodeData['_not']) { + point.label = 'NOT'; + } else if (nodeData[0]) { + point.label = 'AND'; + } else { + console.debug('Error. No boolean value found'); + } + point.id = this.treeIndex++; + const newNode: TreeNode = new TreeNode({ + id: point.id, + expanded: true, + label: point.label, + callerData: {point: point} + }); + parentNode.children.push(newNode); + this.idmap[point.id + ''] = newNode; + if (dataIsArray) { + nodeData.forEach(n => { + this.buildTree(newNode, n); + }); + } else if (nodeData['_not']) { + this.buildTree(newNode, nodeData['_not']); + } else if (nodeData[0]) { + for (const key of Object.keys(nodeData)) { + this.buildTree(newNode, nodeData[key]); + } + } else { + console.debug('Error building tree'); + } + } + + buildTreeRecord = (nodeData: any, point: any, parentNode) => { + point.typeLabel = this.recordAttrDefs[nodeData._attr].label(); + point.typeId = nodeData._attr; + point.valueId = nodeData._val; + this.fetchAttrs.push({'-and' : {ctype : nodeData._attr, code : nodeData._val}}); + point.id = this.treeIndex++; + const newNode: TreeNode = new TreeNode({ + id: point.id, + expanded: true, + label: null, + callerData: {point: point} + }); + parentNode.children.push(newNode); + this.idmap[point.id + ''] = newNode; + } + + createNewTree = () => { + this.changesMade = true; + this.treeIndex = 2; + if (this.newPointType === 'bool') { + this.addBooleanRootNode(this.newPoint.values.boolOp); + } else { + this.addRecordRootNode(this.newPoint); + } + } + + addBooleanRootNode = (boolOp: any) => { + const point = { id: 1, label: boolOp, children: []}; + const node: TreeNode = new TreeNode({id: 1, label: boolOp, children: [], + callerData: {point: point}}); + this.idmap['1'] = node; + this.tree = new Tree(node); + return node; + } + + addRecordRootNode = (record: any) => { + const point = { id: 1, expanded: true, children: [], label: null, typeLabel: null, + typeId: null, valueLabel: null, valueId: null}; + point.typeLabel = record.values.typeLabel; + point.typeId = record.values.typeId; + point.valueLabel = record.values.valueLabel; + point.valueId = record.values.valueId; + const fullLabel = this.buildLabel(point.typeLabel, point.typeId, point.valueLabel, point.valueId); + const node: TreeNode = new TreeNode({ id: 1, label: fullLabel, children: [], + callerData: {point: point}}); + this.idmap['1'] = node; + this.tree = new Tree(node); + return node; + } + + buildLabel = (tlbl, tid, vlbl, vid) => { + return tlbl + ' (' + tid + ') => ' + vlbl + ' (' + vid + ')'; + } + + nodeClicked(node: TreeNode) { + console.debug('Node clicked on: ' + node.label); + } + + deleteTree = () => { + this.tree = null; + this.idmap = {}; + this.treeIndex = 2; + this.changesMade = true; + } + + deleteNode = () => { + this.changesMade = true; + if (this.isRootNode()) { + this.deleteTree(); + } else { + this.tree.removeNode(this.tree.selectedNode()); + } + } + + hasSelectedNode(): boolean { + if (this.tree) { + return Boolean(this.tree.selectedNode()); + } + } + + isRootNode(): boolean { + const node = this.tree.selectedNode(); + if (node && this.tree.findParentNode(node) === null) { + return true; + } + return false; + } + + selectedIsBool(): boolean { + if (!this.tree) { return false; } + if (this.tree.selectedNode()) { + const label = this.tree.selectedNode().label; + if (label === 'AND' || label === 'NOT' || label === 'OR') { return true; } + } + return false; + } + + // Disable this: + // 1. if no node selected + // 2. if trying to add to a non-boolean record + // 3. if trying to add more than 1 child to a NOT + // 4. if trying to add NOT to an existing NOT + // 5. if trying to add before user has made selection of new value or operator + addButtonDisabled(): boolean { + if (!this.hasSelectedNode()) { return true; } + if (!this.selectedIsBool()) { return true; } + if ((this.tree.selectedNode().label === 'NOT') && + (this.tree.selectedNode().children.length > 0)) { return true; } + if ((this.tree.selectedNode().label === 'NOT') && + (this.newPoint.values.boolOp === 'NOT')) { return true; } + if (this.newPointType === 'attr' && + (this.newPoint.values.typeId.length > 0) && + (this.newPoint.values.valueId.length > 0)) { return false; } + if (this.newPointType === 'bool' && + (this.newPoint.values.boolOp.length > 0)) { return false; } + return true; + } + + // Disable this: + // 1. if no node selected + // 2. if trying to replace a boolean with a non-boolean or vice versa + // 3. if trying to replace before user has made selection of new value or operator + replaceButtonDisabled(): boolean { + if (!this.hasSelectedNode()) { return true; } + if (this.newPointType === 'attr' && !this.selectedIsBool() && + (this.newPoint.values.typeId.length > 0) && + (this.newPoint.values.valueId.length > 0)) { return false; } + if (this.newPointType === 'bool' && this.selectedIsBool() && + (this.newPoint.values.boolOp.length > 0)) { return false; } + return true; + } + + // disabled until you select a type and select values for that type + newTreeButtonDisabled(): boolean { + if ((this.newPointType === 'bool') && (this.newPoint.values.boolOp.length > 0)) { + return false; + } + if ((this.newPointType === 'attr') && (this.newPoint.values.typeId.length > 0) && + (this.newPoint.values.valueId.length > 0)) { return false; } + return true; + } + + back() { + this.router.navigate(['/staff/admin/server/config/coded_value_map']); + } + + saveTree = () => { + const recordToSave = this.idl.create('ccraed'); + recordToSave.coded_value(this.currentId); + const expression = this.exportTree(this.idmap['1']); + const jsonStr = JSON.stringify(expression); + recordToSave.definition(jsonStr); + if (this.noSavedTreeData) { + this.pcrud.create(recordToSave).subscribe( + ok => { + this.saveSuccess.current().then(str => this.toast.success(str)); + this.noSavedTreeData = false; + }, + err => { + this.saveFail.current().then(str => this.toast.danger(str)); + } + ); + } else { + this.pcrud.update(recordToSave).subscribe( + async (ok) => { + this.saveSuccess.current().then(str => this.toast.success(str)); + }, + async (err) => { + this.saveFail.current().then(str => this.toast.danger(str)); + } + ); + } + } + + exportTree(node: TreeNode): any { + const lbl = node.label; + if ((lbl !== 'NOT') && (lbl !== 'AND') && (lbl !== 'OR')) { + const retval = {_attr: node.callerData.point.typeId, _val: node.callerData.point.valueId}; + return retval; + } + if (lbl === 'NOT') { + return {_not : this.exportTree(node.children[0])}; // _not nodes may only have one child + } + let compiled; + for (let i = 0; i < node.children.length; i++) { + const child = node.children[i]; + if (!compiled) { + if (node.label === 'OR') { + compiled = []; + } else { + compiled = {}; + } + } + compiled[i] = this.exportTree(child); + } + return compiled; + } + + addChildNode(replace?: boolean) { + const targetNode: TreeNode = this.tree.selectedNode(); + this.changesMade = true; + const point = { + id: null, + expanded: true, + children: [], + parent: targetNode.id, + label: null, + typeLabel: null, + typeId: null, + valueLabel: null, + valueId: null, + }; + + const node: TreeNode = new TreeNode({ + callerData: {point: point}, + id: point.id, + label: null + }); + + if (this.newPoint.values.pointType === 'bool') { + point.label = this.newPoint.values.boolOp; + node.label = point.label; + } else { + point.typeLabel = this.newPoint.values.typeLabel; + point.valueLabel = this.newPoint.values.valueLabel; + point.typeId = this.newPoint.values.typeId; + point.valueId = this.newPoint.values.valueId; + } + if (replace) { + if (this.newPoint.values.pointType === 'bool') { + targetNode.label = point.label; + } else { + targetNode.label = this.buildLabel(point.typeLabel, point.typeId, point.valueLabel, + point.valueId); + } + targetNode.callerData.point = point; + } else { + point.id = this.treeIndex; + node.id = this.treeIndex++; + if (this.newPoint.values.pointType === 'bool') { + node.label = point.label; + } else { + node.label = this.buildLabel(point.typeLabel, point.typeId, point.valueLabel, + point.valueId); + } + point.parent = targetNode.id; + targetNode.children.push(node); + this.idmap[point.id + ''] = node; + } + } + + } diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-new.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-new.component.html new file mode 100644 index 0000000000..1d08983833 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-new.component.html @@ -0,0 +1,48 @@ +
+
+

+ Record Attribute +

+

+ Boolean Operator +

+
+
+ +
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+ +
+
Operator:
+
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-new.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-new.component.ts new file mode 100644 index 0000000000..38408cdb4d --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/coded-value-maps/composite-new.component.ts @@ -0,0 +1,84 @@ +import {Component, OnInit, Input, ViewChild} from '@angular/core'; +import {IdlObject} from '@eg/core/idl.service'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {ComboboxComponent, ComboboxEntry} from '@eg/share/combobox/combobox.component'; + +export class CompositeNewPointValues { + pointType: string; + boolOp: string; + typeLabel: string; + typeId: string; + valueLabel: string; + valueId: string; +} + +@Component({ + selector: 'eg-composite-new-point', + templateUrl: 'composite-new.component.html' +}) +export class CompositeNewPointComponent implements OnInit { + + public values: CompositeNewPointValues; + + attrTypeDefs: IdlObject[]; + attrValDefs: IdlObject[]; + attrTypes: ComboboxEntry[]; + attrVals: ComboboxEntry[]; + + @Input() set pointType(type_: string) { + this.values.pointType = type_; + this.values.boolOp = ''; + this.values.valueLabel = ''; + this.values.valueId = ''; + this.values.typeId = ''; + this.values.typeLabel = ''; + } + + @ViewChild('valComboBox', {static: false}) valComboBox: ComboboxComponent; + + constructor( + private pcrud: PcrudService + ) { + this.values = new CompositeNewPointValues(); + this.attrTypeDefs = []; + this.attrTypes = []; + } + + ngOnInit() { + this.pcrud.retrieveAll('crad', {order_by: {crad: 'label'}}) + .subscribe(attr => { + this.attrTypeDefs.push(attr); + this.attrTypes.push({id: attr.name(), label: attr.label()}); + }); + } + + typeChange(evt) { + this.values.typeId = evt.id; + this.values.typeLabel = evt.label; + this.valComboBox.selected = null; // reset other combobox + this.values.valueId = ''; // don't allow save with old valueId or valueLabel + this.values.valueLabel = ''; + this.attrVals = []; + this.attrValDefs = []; + this.pcrud.search('ccvm', {'ctype': evt.id}, + {flesh: 1, flesh_fields: {ccvm: ['composite_def', 'ctype']} }).subscribe( + data => { + this.attrValDefs.push(data); + this.attrVals.push({id: data.code(), label: data.value()}); + }, + err => { + console.debug(err); + this.attrVals = []; + this.attrValDefs = []; + } + ); + } + + valueChange(evt) { + if (evt) { + this.values.valueId = evt.id; + this.values.valueLabel = evt.label; + } + } +} + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts index 961d8060bd..caadbcb897 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts @@ -13,6 +13,10 @@ const routes: Routes = [{ path: 'actor/org_unit_type', component: OrgUnitTypeComponent }, { + path: 'config/coded_value_map', + loadChildren: () => + import('./coded-value-maps/coded-value-maps.module').then(m => m.CodedValueMapsModule) +}, { path: 'config/floating_group', loadChildren: () => import('./floating-group/floating-group.module').then(m => m.FloatingGroupModule) -- 2.11.0