start fleshing out more of the search form
authorGalen Charlton <gmc@equinoxinitiative.org>
Thu, 16 Jan 2020 23:50:02 +0000 (18:50 -0500)
committerGalen Charlton <gmc@equinoxinitiative.org>
Thu, 16 Jan 2020 23:50:02 +0000 (18:50 -0500)
- start adding appropriate input widgets based on
  field type
- set up proof of concept of integrating search terms
  from form with grid filters
- do some renaming and defining interfaces

Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/eg2/src/app/staff/acq/search/acq-search.component.html
Open-ILS/src/eg2/src/app/staff/acq/search/acq-search.component.ts
Open-ILS/src/eg2/src/app/staff/acq/search/acq-search.service.ts
Open-ILS/src/eg2/src/app/staff/acq/search/purchase-order-results.component.ts

index 33d6487..75d10d9 100644 (file)
@@ -4,34 +4,42 @@
 <div class="row">
   <div class="ml-auto mr-3"><a i18n href="/eg/staff/acq/legacy/search/unified">Legacy Search Interface</a></div>
 </div>
-<div class="row">
+<div class="row" *ngFor="let t of searchTerms">
   <div class="col-lg-3">
-    <select class="form-control" id="selected-search-term" [ngModelOptions]="{standalone: true}" [(ngModel)]="selectedSearchTerm">
+    <select class="form-control" id="selected-search-term" [ngModelOptions]="{standalone: true}" [(ngModel)]="t.field">
       <option disabled="disabled" i18n>Select Search Field</option>
-      <optgroup *ngFor="let g of hints" label="{{availableSearchTerms[g]['__label']}}">
-        <option *ngFor="let o of availableSearchTerms[g]['__fields']" value="{{g}}:{{o}}">
-          {{availableSearchTerms[g][o].label}}
+      <optgroup *ngFor="let g of hints" label="{{availableSearchFields[g]['__label']}}">
+        <option *ngFor="let o of availableSearchFields[g]['__fields']" value="{{g}}:{{o}}">
+          {{availableSearchFields[g][o].label}}
         </option>
       </optgroup>
     </select>
   </div>
-  <div class="col-lg-3">
-    <select class="form-control" id="selected-search-op" [ngModelOptions]="{standalone: true}" [(ngModel)]="selectedSearchOp">
+  <div class="col-lg-2">
+    <select class="form-control" id="selected-search-op" [ngModelOptions]="{standalone: true}" [(ngModel)]="t.op">
       <option i18n value="">is</option>
       <option i18n value="__not">is NOT</option>
-      <option i18n value="__fuzzy" [disabled]="searchTermDatatypes[selectedSearchTerm] != 'text'">contains</option>
-      <option i18n value="__not,__fuzzy" [disabled]="searchTermDatatypes[selectedSearchTerm] != 'text'">does NOT contain</option>
-      <option i18n value="__lte" [disabled]="searchTermDatatypes[selectedSearchTerm] != 'timestamp'">is on or BEFORE</option>
-      <option i18n value="__gte" [disabled]="searchTermDatatypes[selectedSearchTerm] != 'timestamp'">is on or AFTER</option>
+      <option i18n value="__fuzzy" [disabled]="searchTermDatatypes[t.field] != 'text'">contains</option>
+      <option i18n value="__not,__fuzzy" [disabled]="searchTermDatatypes[t.field] != 'text'">does NOT contain</option>
+      <option i18n value="__lte" [disabled]="searchTermDatatypes[t.field] != 'timestamp'">is on or BEFORE</option>
+      <option i18n value="__gte" [disabled]="searchTermDatatypes[t.field] != 'timestamp'">is on or AFTER</option>
       <option i18n value="__in">matches a term from a file</option>
     </select>
   </div>
   <div class="col-lg-3">
+    <input [(ngModel)]="t.value1" type="text" *ngIf="searchTermDatatypes[t.field] == 'text'" class="form-control" />
+    <input [(ngModel)]="t.value1" type="numeric" *ngIf="searchTermDatatypes[t.field] == 'money'" class="form-control" />
+    <eg-org-select *ngIf="searchTermDatatypes[t.field] == 'org_unit'"
+      (onChange)="setOrgUnitSearchValue($event, t)">
+    </eg-org-select>
   </div>
 </div>
 <div class="row">
   <div class="col-lg-2">
-    <button class="form-control" i18n>Add Search Term</button>
+    <button class="form-control" (click)="addSearchTerm()" i18n>Add Search Term</button>
+  </div>
+  <div class="col-lg-2">
+    <button class="form-control btn-success" (click)="submitSearch()" i18n>Search</button>
   </div>
 </div>
 <div class="row">
index fd4bfa5..6a23ce5 100644 (file)
@@ -1,9 +1,10 @@
-import {Component, OnInit, AfterViewInit, ViewChild} from '@angular/core';
+import {Component, OnInit, AfterViewInit, ViewChild, ViewChildren, QueryList} from '@angular/core';
 import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap';
 import {Router, ActivatedRoute} from '@angular/router';
 import {StaffCommonModule} from '@eg/staff/common.module';
 import {IdlService, IdlObject} from '@eg/core/idl.service';
 import {PcrudService} from '@eg/core/pcrud.service';
+import {AcqSearchTerm} from './acq-search.service';
 import {LineitemResultsComponent} from './lineitem-results.component';
 import {PurchaseOrderResultsComponent} from './purchase-order-results.component';
 import {InvoiceResultsComponent} from './invoice-results.component';
@@ -15,18 +16,18 @@ import {PicklistResultsComponent} from './picklist-results.component';
 
 export class AcqSearchComponent implements OnInit, AfterViewInit {
 
-    selectedSearchTerm: String;
-    selectedSearchOp: String;
-
     hints = ['jub', 'acqpl', 'acqpo', 'acqinv', 'acqlid'];
-    availableSearchTerms = {};
+    availableSearchFields = {};
     searchTermDatatypes = {};
     searchType = '';
     validSearchTypes = ['lineitems', 'purchaseorders', 'invoices', 'selectionlists'];
     defaultSearchType = 'lineitems';
 
+    searchTerms: AcqSearchTerm[] = [];
+
     onTabChange: ($event: NgbTabChangeEvent) => void;
     @ViewChild('acqSearchTabs', { static: true }) tabs: NgbTabset;
+    @ViewChildren(PurchaseOrderResultsComponent) poResults: QueryList<PurchaseOrderResultsComponent>;
 
     constructor(
         private router: Router,
@@ -37,8 +38,6 @@ export class AcqSearchComponent implements OnInit, AfterViewInit {
 
     ngOnInit() {
         const self = this;
-        this.selectedSearchTerm = '';
-        this.selectedSearchOp = '';
 
         const searchTypeParam = this.route.snapshot.paramMap.get('searchtype');
 
@@ -74,24 +73,43 @@ export class AcqSearchComponent implements OnInit, AfterViewInit {
                         }
                     }
                 );
-                self.availableSearchTerms[hint] = o;
+                self.availableSearchFields[hint] = o;
             }
         );
 
         this.hints.push('acqlia');
-        this.availableSearchTerms['acqlia'] = {'__label': this.idl.classes.acqlia.label, '__fields': []};
+        this.availableSearchFields['acqlia'] = {'__label': this.idl.classes.acqlia.label, '__fields': []};
         this.pcrud.retrieveAll('acqliad', {'order_by': {'acqliad': 'id'}})
         .subscribe(liad => {
-            this.availableSearchTerms['acqlia']['__fields'].push('' + liad.id());
-            this.availableSearchTerms['acqlia'][liad.id()] = {
+            this.availableSearchFields['acqlia']['__fields'].push('' + liad.id());
+            this.availableSearchFields['acqlia'][liad.id()] = {
                 label: liad.description(),
                 datatype: 'text'
             };
             this.searchTermDatatypes['acqlia:' + liad.id()] = 'text';
         });
 
+        this.addSearchTerm();
     }
 
     ngAfterViewInit() {}
 
+    addSearchTerm() {
+        this.searchTerms.push({ field: '', op: '', value1: '', value2: '' });
+    }
+
+    setOrgUnitSearchValue(org: IdlObject, term: AcqSearchTerm) {
+        if (org == null) {
+            term.value1 = '';
+        } else {
+            term.value1 = org.id();
+        }
+    }
+
+    submitSearch() {
+        if (this.searchType === 'purchaseorders') {
+            this.poResults.forEach(poResult => poResult.doSearch(this.searchTerms));
+        }
+    }
+
 }
index 97ead98..778a8c4 100644 (file)
@@ -72,19 +72,43 @@ const operatorMap = {
     'like': '__fuzzy',
 };
 
+export interface AcqSearchTerm {
+    field: string;
+    op: string;
+    value1: string;
+    value2: string;
+}
+
 @Injectable()
 export class AcqSearchService {
 
+    _terms: AcqSearchTerm[] = [];
+
     constructor(
         private net: NetService,
         private auth: AuthService
     ) {
     }
 
+    setSearchTerms(terms: AcqSearchTerm[]) {
+        this._terms = terms;
+    }
+
     generateAcqSearch(searchType, filters): any {
         const baseSearch = JSON.parse(JSON.stringify(defaultSearch[searchType])); // deep copy
         const coreRecType = Object.keys(defaultSearch[searchType])[0];
 
+        // handle supplied search terms
+        this._terms.forEach(term => {
+            const searchTerm: Object = {};
+            const searchField = term.field.split(':')[1];
+            searchTerm[searchField] = term.value1;
+            if (term.op !== '') {
+                searchTerm[term.op] = true;
+            }
+            baseSearch[coreRecType].push(searchTerm);
+        });
+
         // handle grid filters
         // note that date filters coming from the grid do not need
         // to worry about __castdate because the grid filter supplies
index 3d688c6..9c9725f 100644 (file)
@@ -8,7 +8,7 @@ import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
 import {GridComponent} from '@eg/share/grid/grid.component';
 import {GridDataSource} from '@eg/share/grid/grid';
-import {AcqSearchService} from './acq-search.service';
+import {AcqSearchService, AcqSearchTerm} from './acq-search.service';
 
 @Component({
   selector: 'eg-purchase-order-results',
@@ -32,4 +32,8 @@ export class PurchaseOrderResultsComponent implements OnInit {
         this.gridSource = this.acqSearch.getAcqSearchDataSource('purchase_order');
     }
 
+    doSearch(terms: AcqSearchTerm[]) {
+        this.acqSearch.setSearchTerms(terms);
+        this.purchaseOrderResultsGrid.reload();
+    }
 }