LP#1779158 Match set UI continued
authorBill Erickson <berickxx@gmail.com>
Tue, 24 Jul 2018 19:51:21 +0000 (15:51 -0400)
committerBill Erickson <berickxx@gmail.com>
Thu, 11 Oct 2018 18:56:30 +0000 (14:56 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/staff/cat/vandelay/match-set-expression.component.html
Open-ILS/src/eg2/src/app/staff/cat/vandelay/match-set-expression.component.ts
Open-ILS/src/eg2/src/app/staff/cat/vandelay/match-set-new-point.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/cat/vandelay/match-set-new-point.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/cat/vandelay/vandelay.module.ts

index 98404ee..fe7d817 100644 (file)
     <div class="row ml-2 mt-4">
       <span class="mr-2" i18n>Add New:</span>
       <button class="btn btn-outline-dark mr-2" *ngIf="matchSetType=='biblio'"
-        (click)="setNewPointType('attr')" i18n>Record Attribute</button>
+        (click)="newPointType='attr'" i18n>Record Attribute</button>
       <button class="btn btn-outline-dark mr-2" 
-        (click)="setNewPointType('marc')" i18n>MARC Tag and Subfield</button>
+        (click)="newPointType='marc'" i18n>MARC Tag and Subfield</button>
       <button class="btn btn-outline-dark mr-2" *ngIf="matchSetType=='authority'"
-        (click)="setNewPointType('heading')" i18n>Normalized Authority Heading</button>
+        (click)="newPointType='heading'" i18n>Normalized Authority Heading</button>
       <button class="btn btn-outline-dark mr-2" 
-        (click)="setNewPointType('bool')" i18n>Boolean Operator</button>
+        (click)="newPointType='bool'" i18n>Boolean Operator</button>
     </div>
-    <div class="row ml-2 mt-4 p-2 border border-secondary" *ngIf="newPointType">
-      <div class="col-lg-12 common-form striped-odd form-validated">
-        <ng-container *ngIf="newPointType=='attr'">
-          <div class="row mb-1">
-            <div class="col-lg-3" i18n>Record Attribute:</div>
-            <div class="col-lg-4">
-              <eg-combobox [entries]="bibAttrDefEntries"
-                [required]="true" 
-                (onChange)="newRecordAttr=$event ? $event.id : ''"
-                placeholder="Record Attribute..." i18n-placeholder>                       
-              </eg-combobox>  
-            </div>
-          </div>
-        </ng-container>
-        <ng-container *ngIf="newPointType=='marc'">
-          <div class="row mb-1">
-            <div class="col-lg-3" i18n>Tag:</div>
-            <div class="col-lg-2">
-              <input required type="text" class="form-control" [(ngModel)]="newMarcTag"/>
-            </div>
-          </div>
-          <div class="row mb-1">
-            <div class="col-lg-3" i18n>Subfield ‡:</div>
-            <div class="col-lg-2">
-              <input required type="text" class="form-control" [(ngModel)]="newMarcSf"/>
-            </div>
-          </div>
-        </ng-container>
-        <ng-container *ngIf="newPointType=='heading'">
-          <div class="row mb-1">
-            <div class="col-lg-3" i18n>Normalized Heading:</div>
-            <div class="col-lg-2">
-              <input type="checkbox" class="form-check-input" checked disabled/>
-            </div>
-          </div>
-        </ng-container>
-        <ng-container *ngIf="newPointType!='bool'">
-          <div class="row mb-1">
-            <div class="col-lg-3">Match Score:</div>
-            <div class="col-lg-2">
-              <input required type="number" class="form-control" 
-                [(ngModel)]="newMatchScore" step="0.1"/>
-            </div>
-          </div>
-          <div class="row mb-1">
-            <div class="col-lg-3">Negate:</div>
-            <div class="col-lg-2">
-              <input type="checkbox" 
-                class="form-check-input" [(ngModel)]="newNegate"/>
-            </div>
-          </div>
-        </ng-container>
-        <ng-container *ngIf="newPointType=='bool'">
-          <div class="row mb-1">
-            <div class="col-lg-3">Operator:</div>
-            <div class="col-lg-2">
-              <select class="form-control" [(ngModel)]="newBoolOp">
-                <option value='AND' i18n>AND</option>
-                <option value='OR' i18n>OR</option>
-              </select>
-            </div>
-          </div>
-        </ng-container>
-        <div class="row mt-2">
-          <button class="btn btn-success" (click)="addChildNode()" 
-            [disabled]="!selectedIsBool()" i18n>
-            Add To Selected Node
-          </button>
-        </div>
-        <div class="row mt-2 font-italic">
-          <ol i18n>
-            <li>Define a new match point using the above fields.</li>
-            <li>Select a boolean node in the tree.</li>
-            <li>Click the "Add..." button to add the new matchpoint
-              as a child of the selected node.</li>
-          </ol>
-        </div>
-      </div>
+    <eg-match-set-new-point #newPoint [pointType]="newPointType">
+    </eg-match-set-new-point>
+    <div class="row mt-2 ml-2" *ngIf="newPointType">
+      <button class="btn btn-success" (click)="addChildNode()" 
+        [disabled]="!selectedIsBool()" i18n>
+        Add To Selected Node
+      </button>
+    </div>
+    <div class="row mt-2 ml-2 font-italic" *ngIf="newPointType">
+      <ol i18n>
+        <li>Define a new match point using the above fields.</li>
+        <li>Select a boolean node in the tree.</li>
+        <li>Click the "Add..." button to add the new matchpoint
+          as a child of the selected node.</li>
+      </ol>
     </div>
   </div>
   <div class="col-lg-5">
index 5a77ed9..cba3f25 100644 (file)
@@ -1,10 +1,13 @@
 import {Component, OnInit, ViewChild, AfterViewInit, Input} from '@angular/core';
 import {IdlObject, IdlService} from '@eg/core/idl.service';
 import {PcrudService} from '@eg/core/pcrud.service';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
 import {OrgService} from '@eg/core/org.service';
 import {Tree, TreeNode} from '@eg/share/tree/tree';
 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
 import {StringService} from '@eg/share/string/string.service';
+import {MatchSetNewPointComponent} from './match-set-new-point.component';
 
 @Component({
   selector: 'eg-match-set-expression',
@@ -25,48 +28,30 @@ export class MatchSetExpressionComponent implements OnInit {
 
     tree: Tree;
     initDone: boolean;
-    matchSetPoints: {[id: string]: IdlObject};
     matchSetType: string;
     changesMade: boolean;
 
     // Current type of new match point
     newPointType: string;
-    newRecordAttr: string;
-    newMatchScore: number;
-    newNegate: boolean;
-    newMarcTag: string;
-    newMarcSf: string;
-    newHeading: string;
-    newBoolOp: string;
     newId: number;
 
-    bibAttrDefs: IdlObject[];
-    bibAttrDefEntries: ComboboxEntry[];
+    @ViewChild('newPoint') newPoint: MatchSetNewPointComponent;
 
     constructor(
         private idl: IdlService,
         private pcrud: PcrudService,
+        private net: NetService,
+        private auth: AuthService,
         private org: OrgService,
         private strings: StringService
     ) {
-        this.bibAttrDefs = [];
-        this.bibAttrDefEntries = [];
-        this.matchSetPoints = {};
         this.newId = -1;
     }
 
-    ngOnInit() {
-
-        this.pcrud.retrieveAll('crad', {order_by: {crad: 'label'}})
-        .subscribe(attr => {
-            this.bibAttrDefs.push(attr);
-            this.bibAttrDefEntries.push({id: attr.name(), label: attr.label()});
-        });
-    }
+    ngOnInit() {}
 
     refreshTree(): Promise<any> {
         if (!this.matchSet_) { return; }
-        this.matchSetPoints = {};
 
         return this.pcrud.search('vmsp',
             {match_set: this.matchSet_.id()}, {}, 
@@ -78,11 +63,9 @@ export class MatchSetExpressionComponent implements OnInit {
             const idmap: any = {};
             points.forEach(point => {
 
-                // Track the from-database point objects
-                this.matchSetPoints[point.id()] = point;
-
                 point.negate(point.negate() === 't' ? true : false);
                 point.heading(point.heading() === 't' ? true : false);
+                point.children([]);
 
                 const node = new TreeNode({
                     id: point.id(),
@@ -127,11 +110,6 @@ export class MatchSetExpressionComponent implements OnInit {
     deleteNode() {
         this.changesMade = true;
         const node = this.tree.selectedNode()
-        const point = this.matchSetPoints[node.id];
-        if (point) {
-            // point won't be cached if it's new during this session.
-            point.isdeleted(true);
-        }
         this.tree.removeNode(node);
     }
 
@@ -156,24 +134,27 @@ export class MatchSetExpressionComponent implements OnInit {
         point.isnew(true);
         point.parent(pnode.id);
         point.match_set(this.matchSet_.id());
+        point.children([]);
+
+        const ptype = this.newPoint.values.pointType;
 
-        if (this.newPointType === 'bool') {
-            point.bool_op(this.newBoolOp);
+        if (ptype === 'bool') {
+            point.bool_op(this.newPoint.values.boolOp);
 
         } else {
 
-            if (this.newPointType == 'attr') {
-                point.svf(this.newRecordAttr);
+            if (ptype == 'attr') {
+                point.svf(this.newPoint.values.recordAttr);
 
-            } else if (this.newPointType == 'marc') {
-                point.tag(this.newMarcTag);
-                point.subfield(this.newMarcSf ? this.newMarcSf : null);
-            } else if (this.newPointType == 'heading') {
+            } else if (ptype == 'marc') {
+                point.tag(this.newPoint.values.marcTag);
+                point.subfield(this.newPoint.values.marcSf);
+            } else if (ptype == 'heading') {
                 point.heading(true);
             }
 
-            point.negate(this.newNegate);
-            point.quality(this.newMatchScore);
+            point.negate(this.newPoint.values.negate);
+            point.quality(this.newPoint.values.matchScore);
         }
 
         const node: TreeNode = new TreeNode({
@@ -185,16 +166,6 @@ export class MatchSetExpressionComponent implements OnInit {
         this.setNodeLabel(node, point).then(() => pnode.children.push(node));
     }
 
-    setNewPointType(type_: string) {
-        this.newPointType = type_;
-        this.newRecordAttr = '';
-        this.newMatchScore = 1;
-        this.newNegate = false;
-        this.newMarcTag = '';
-        this.newMarcSf = '';
-        this.newBoolOp = 'AND';
-    }
-
     expressionAsString(): string {
         if (!this.tree) { return ''; }
 
@@ -214,21 +185,34 @@ export class MatchSetExpressionComponent implements OnInit {
         return renderNode(this.tree.rootNode);
     }
 
+    // Server API deletes and recreates the tree on update.
+    // It manages parent/child relationships via the children array.
+    // We only need send the current tree in a form the API recognizes.
     saveTree(): Promise<any> {
 
-        // New nodes
-        let nodes = this.tree.nodeList()
-            .filter(node => node.callerData.point.isnew())
-            .map(node => node.callerData.point);
 
-        // Deleted nodes
-        nodes = nodes.concat(
-            Object.values(this.matchSetPoints)
-            .filter(point => point.isdeleted())
-        );
+        const compileTree = (node?: TreeNode) => {
+
+            if (!node) { node = this.tree.rootNode; }
+
+            const point = node.callerData.point;
+
+            node.children.forEach(child =>
+                point.children().push(compileTree(child)));
 
-        return this.pcrud.autoApply(nodes)
-            .toPromise().then(() => this.refreshTree())
+            return point;
+        };
+
+        const rootPoint: IdlObject = compileTree();
+
+        return this.net.request(
+            'open-ils.vandelay',
+            'open-ils.vandelay.match_set.update',
+            this.auth.token(), this.matchSet_.id(), rootPoint
+        ).toPromise().then(
+            ok =>this.refreshTree(),
+            err => console.error(err)
+        );
     }
 }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/cat/vandelay/match-set-new-point.component.html b/Open-ILS/src/eg2/src/app/staff/cat/vandelay/match-set-new-point.component.html
new file mode 100644 (file)
index 0000000..1de54c8
--- /dev/null
@@ -0,0 +1,66 @@
+<div class="row ml-2 mt-4 p-2 border border-secondary" *ngIf="values.pointType">
+  <div class="col-lg-12 common-form striped-odd form-validated">
+    <ng-container *ngIf="values.pointType=='attr'">
+      <div class="row mb-1">
+        <div class="col-lg-3" i18n>Record Attribute:</div>
+        <div class="col-lg-4">
+          <eg-combobox [entries]="bibAttrDefEntries"
+            [required]="true" 
+            (onChange)="values.recordAttr=$event ? $event.id : ''"
+            placeholder="Record Attribute..." i18n-placeholder>                       
+          </eg-combobox>  
+        </div>
+      </div>
+    </ng-container>
+    <ng-container *ngIf="values.pointType=='marc'">
+      <div class="row mb-1">
+        <div class="col-lg-3" i18n>Tag:</div>
+        <div class="col-lg-2">
+          <input required type="text" class="form-control" [(ngModel)]="values.marcTag"/>
+        </div>
+      </div>
+      <div class="row mb-1">
+        <div class="col-lg-3" i18n>Subfield ‡:</div>
+        <div class="col-lg-2">
+          <input required type="text" class="form-control" [(ngModel)]="values.marcSf"/>
+        </div>
+      </div>
+    </ng-container>
+    <ng-container *ngIf="values.pointType=='heading'">
+      <div class="row mb-1">
+        <div class="col-lg-3" i18n>Normalized Heading:</div>
+        <div class="col-lg-2">
+          <input type="checkbox" class="form-check-input" checked disabled/>
+        </div>
+      </div>
+    </ng-container>
+    <ng-container *ngIf="values.pointType!='bool'">
+      <div class="row mb-1">
+        <div class="col-lg-3">Match Score:</div>
+        <div class="col-lg-2">
+          <input required type="number" class="form-control" 
+            [(ngModel)]="values.matchScore" step="0.1"/>
+        </div>
+      </div>
+      <div class="row mb-1">
+        <div class="col-lg-3">Negate:</div>
+        <div class="col-lg-2">
+          <input type="checkbox" 
+            class="form-check-input" [(ngModel)]="values.negate"/>
+        </div>
+      </div>
+    </ng-container>
+    <ng-container *ngIf="values.pointType=='bool'">
+      <div class="row mb-1">
+        <div class="col-lg-3">Operator:</div>
+        <div class="col-lg-2">
+          <select class="form-control" [(ngModel)]="values.boolOp">
+            <option value='AND' i18n>AND</option>
+            <option value='OR' i18n>OR</option>
+          </select>
+        </div>
+      </div>
+    </ng-container>
+  </div>
+</div>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/cat/vandelay/match-set-new-point.component.ts b/Open-ILS/src/eg2/src/app/staff/cat/vandelay/match-set-new-point.component.ts
new file mode 100644 (file)
index 0000000..8e74602
--- /dev/null
@@ -0,0 +1,60 @@
+import {Component, OnInit, ViewChild, Output, Input} from '@angular/core';
+import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+
+// Can be used to create match_set_point's and match_set_quality's
+export class MatchSetPointValues {
+    pointType: string;
+    recordAttr: string;
+    matchScore: number;
+    negate: boolean;
+    marcTag: string;
+    marcSf: string;
+    heading: string;
+    boolOp: string;
+}
+
+@Component({
+  selector: 'eg-match-set-new-point',
+  templateUrl: 'match-set-new-point.component.html'
+})
+export class MatchSetNewPointComponent implements OnInit {
+
+    public values: MatchSetPointValues;
+
+    bibAttrDefs: IdlObject[];
+    bibAttrDefEntries: ComboboxEntry[];
+
+    // biblio, authority, quality
+    @Input() set pointType(type_: string) { 
+        this.values.pointType = type_;
+        this.values.recordAttr = '';
+        this.values.matchScore = 1;
+        this.values.negate = false;
+        this.values.marcTag = '';
+        this.values.marcSf = '';
+        this.values.boolOp = 'AND';
+    }
+
+    constructor(
+        private idl: IdlService,
+        private pcrud: PcrudService
+    ) {
+        this.values = new MatchSetPointValues();
+        this.bibAttrDefs = [];
+        this.bibAttrDefEntries = [];
+    }
+
+    ngOnInit() {
+        this.pcrud.retrieveAll('crad', {order_by: {crad: 'label'}})
+        .subscribe(attr => {
+            this.bibAttrDefs.push(attr);
+            this.bibAttrDefEntries.push({id: attr.name(), label: attr.label()});
+        });
+    }
+
+    setNewPointType(type_: string) {
+    }
+}
+
index 0215fa9..31dbd92 100644 (file)
@@ -2,6 +2,7 @@ import {NgModule} from '@angular/core';
 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 {VandelayRoutingModule} from './routing.module';
 import {VandelayService} from './vandelay.service';
 import {VandelayComponent} from './vandelay.component';
@@ -19,7 +20,7 @@ import {RecordItemsComponent} from './record-items.component';
 import {MatchSetListComponent} from './match-set-list.component';
 import {MatchSetComponent} from './match-set.component';
 import {MatchSetExpressionComponent} from './match-set-expression.component';
-import {TreeModule} from '@eg/share/tree/tree.module';
+import {MatchSetNewPointComponent} from './match-set-new-point.component';
 
 @NgModule({
   declarations: [
@@ -37,7 +38,8 @@ import {TreeModule} from '@eg/share/tree/tree.module';
     RecordItemsComponent,
     MatchSetListComponent,
     MatchSetComponent,
-    MatchSetExpressionComponent
+    MatchSetExpressionComponent,
+    MatchSetNewPointComponent
   ],
   imports: [
     TreeModule,