lp1811710: toward hopeless UI
authorJason Etheridge <jason@EquinoxInitiative.org>
Fri, 10 May 2019 06:52:01 +0000 (02:52 -0400)
committerChris Sharp <csharp@georgialibraries.org>
Tue, 15 Sep 2020 15:13:15 +0000 (11:13 -0400)
dedicated interface and grid tweaks

Signed-off-by: Jason Etheridge <jason@EquinoxInitiative.org>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Signed-off-by: Terran McCanna <tmccanna@georgialibraries.org>
Signed-off-by: Chris Sharp <csharp@georgialibraries.org>
Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
Open-ILS/src/eg2/src/app/staff/hopeless/hopeless.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/hopeless/hopeless.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/hopeless/hopeless.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/hopeless/routing.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/routing.module.ts
Open-ILS/src/eg2/src/app/staff/share/holds/grid.component.html
Open-ILS/src/eg2/src/app/staff/share/holds/grid.component.ts
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm
Open-ILS/src/templates/staff/admin/local/t_splash.tt2

index b18dda9..7fc28dc 100644 (file)
@@ -38,6 +38,8 @@
       routerLink="/staff/admin/local/config/hold_matrix_matchpoint"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Holdings Template Editor" 
       url="/eg/staff/cat/volcopy/edit_templates"></eg-link-table-link>
+    <eg-link-table-link i18n-label label="Hopeless Holds"
+      routerLink="/staff/hopeless"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Item Alert Suppression" 
       routerLink="/staff/admin/local/actor/copy_alert_suppress"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Item Alert Types" 
diff --git a/Open-ILS/src/eg2/src/app/staff/hopeless/hopeless.component.html b/Open-ILS/src/eg2/src/app/staff/hopeless/hopeless.component.html
new file mode 100644 (file)
index 0000000..4a3d862
--- /dev/null
@@ -0,0 +1,25 @@
+
+<eg-staff-banner bannerText="Hopeless Holds" i18n-bannerText>
+</eg-staff-banner>
+<eg-title i18n-prefix prefix="Hopeless Holds">
+</eg-title>
+
+<div class="input-group"><div class="input-group-prepend">
+  <div class="input-group-text" i18n>Hopeless Date, Start Range:</div>
+  <eg-date-select [initialIso]="startDate" (onChangeAsDate)="changeStartDate($event)">
+  </eg-date-select>
+  <div class="input-group-text" i18n>Hopeless Date, End Range:</div>
+  <eg-date-select [initialIso]="endDate" (onChangeAsDate)="changeEndDate($event)">
+  </eg-date-select>
+</div></div>
+
+<eg-holds-grid
+  hopeless="true" [showHopelessAfter]="startDate" [showHopelessBefore]="endDate"
+  preFetchSetting="hopeless.holds.prefetch"
+  persistKey="hopeless.wide_holds"
+  [defaultSort]="[{name:'request_time',dir:'asc'}]"
+  [initialPickupLib]="workstation_lib"
+  showFields="id,request_time,ucard_barcode,pl_shortname,hold_type,holdable_formats,title,p_label,hold_status,hopeless_date"
+>
+</eg-holds-grid>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/hopeless/hopeless.component.ts b/Open-ILS/src/eg2/src/app/staff/hopeless/hopeless.component.ts
new file mode 100644 (file)
index 0000000..a7a4491
--- /dev/null
@@ -0,0 +1,59 @@
+
+import {Component, OnInit, ViewChild, Input, TemplateRef} from '@angular/core';
+import {IdlObject} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {AuthService} from '@eg/core/auth.service';
+import {FormatService} from '@eg/core/format.service';
+import {Pager} from '@eg/share/util/pager';
+import {DateSelectComponent} from '@eg/share/date-select/date-select.component';
+import {BibRecordService, BibRecordSummary} from '@eg/share/catalog/bib-record.service';
+
+@Component({
+  templateUrl: 'hopeless.component.html'
+})
+export class HopelessComponent implements OnInit {
+
+    startDate: any;
+    endDate: any;
+    workstation_lib: IdlObject;
+
+    changeStartDate(date) {
+        this.startDate = date;
+    }
+
+    changeEndDate(date) {
+        date.setHours(23);
+        date.setMinutes(59);
+        date.setSeconds(59);
+        this.endDate = date;
+    }
+
+    constructor(
+        private pcrud: PcrudService,
+        private auth: AuthService,
+        private format: FormatService,
+        private bib: BibRecordService,
+    ) {}
+
+    ngOnInit() {
+
+        // for the pickup library selector
+        this.workstation_lib = this.auth.user().ws_ou();
+
+        // Default startDate to today - 10 years
+        const sd = new Date();
+        sd.setFullYear( sd.getFullYear() - 10 );
+        this.startDate = sd.toISOString();
+
+        // Default endDate to today.
+        const ed = new Date();
+        ed.setHours(23);
+        ed.setMinutes(59);
+        ed.setSeconds(59);
+        this.endDate = ed.toISOString();
+
+    }
+
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/hopeless/hopeless.module.ts b/Open-ILS/src/eg2/src/app/staff/hopeless/hopeless.module.ts
new file mode 100644 (file)
index 0000000..293d630
--- /dev/null
@@ -0,0 +1,24 @@
+import {NgModule} from '@angular/core';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {CatalogCommonModule} from '@eg/share/catalog/catalog-common.module';
+import {HopelessRoutingModule} from './routing.module';
+import {HopelessComponent} from './hopeless.component';
+import {HoldsModule} from '@eg/staff/share/holds/holds.module';
+
+@NgModule({
+  declarations: [
+    HopelessComponent
+  ],
+  imports: [
+    StaffCommonModule,
+    CatalogCommonModule,
+    HopelessRoutingModule,
+    HoldsModule
+  ],
+  providers: [
+  ]
+})
+
+export class HopelessModule {
+
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/hopeless/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/hopeless/routing.module.ts
new file mode 100644 (file)
index 0000000..444471c
--- /dev/null
@@ -0,0 +1,16 @@
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {HopelessComponent} from './hopeless.component';
+
+const routes: Routes = [{
+  path: '',
+  component: HopelessComponent
+}];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+  providers: []
+})
+
+export class HopelessRoutingModule {}
index 86428ba..edff9a8 100644 (file)
@@ -52,6 +52,9 @@ const routes: Routes = [{
     loadChildren: () =>
       import('./sandbox/sandbox.module').then(m => m.SandboxModule)
   }, {
+    path: 'hopeless',
+    loadChildren : '@eg/staff/hopeless/hopeless.module#HopelessModule'
+  }, {
     path: 'admin',
     loadChildren: () =>
       import('./admin/routing.module').then(m => m.AdminRoutingModule)
index 9cead96..850f097 100644 (file)
@@ -33,6 +33,7 @@
 
     <eg-grid #holdsGrid [dataSource]="gridDataSource" [sortable]="true"
       [useLocalSort]="enablePreFetch" [cellTextGenerator]="cellTextGenerator"
+      [showFields]="showFields"
       [multiSortable]="true" [persistKey]="persistKey"
       (onRowActivate)="showDetail($event)">
 
         i18-group group="Hold" i18n-label label="Print Holds"
         (onClick)="printHolds()"></eg-grid-toolbar-action>
 
+      <eg-grid-toolbar-action *ngIf="hopeless"
+        i18-group group="Item" i18n-label label="View/Place Orders"
+        [disableOnRows]="metaRecordHoldsSelected"
+        (onClick)="showOrder($event)"></eg-grid-toolbar-action>
+
+      <eg-grid-toolbar-action *ngIf="hopeless"
+        i18-group group="Item" i18n-label label="Add Holdings"
+        [disableOnRows]="metaRecordHoldsSelected"
+        (onClick)="addVolume($event)"></eg-grid-toolbar-action>
+
+      <eg-grid-toolbar-action *ngIf="hopeless"
+        i18-group group="Item" i18n-label label="Show in Catalog"
+        (onClick)="showTitle($event)"></eg-grid-toolbar-action>
+
       <eg-grid-column i18n-label label="Hold ID" path='id' [index]="true" datatype="id">
       </eg-grid-column>
 
index 01b44bf..5828243 100644 (file)
@@ -19,6 +19,7 @@ import {HoldTransferDialogComponent} from './transfer-dialog.component';
 import {HoldCancelDialogComponent} from './cancel-dialog.component';
 import {HoldManageDialogComponent} from './manage-dialog.component';
 import {PrintService} from '@eg/share/print/print.service';
+import {HoldingsService} from '@eg/staff/share/holdings/holdings.service';
 
 /** Holds grid with access to detail page and other actions */
 
@@ -32,6 +33,10 @@ export class HoldsGridComponent implements OnInit {
     @Input() initialPickupLib: number | IdlObject;
     @Input() hidePickupLibFilter: boolean;
 
+    // If true, only retrieve holds with a Hopeless Date
+    // and enable related Actions
+    @Input() hopeless: boolean;
+
     // Grid persist key
     @Input() persistKey: string;
 
@@ -49,6 +54,9 @@ export class HoldsGridComponent implements OnInit {
     // [{name: fname, dir: 'asc'}, {name: fname2, dir: 'desc'}]
     @Input() defaultSort: any[];
 
+    // To pass through to the underlying eg-grid
+    @Input() showFields: string;
+
     mode: 'list' | 'detail' | 'manage' = 'list';
     initDone = false;
     holdsCount: number;
@@ -112,14 +120,34 @@ export class HoldsGridComponent implements OnInit {
         }
     }
 
+
     cellTextGenerator: GridCellTextGenerator;
 
+    // Include holds marked Hopeless on or after this date.
+    _showHopelessAfter: Date;
+    @Input() set showHopelessAfter(show: Date) {
+        this._showHopelessAfter = show;
+        if (this.initDone) { // reload on update
+            this.holdsGrid.reload();
+        }
+    }
+
+    // Include holds marked Hopeless on or before this date.
+    _showHopelessBefore: Date;
+    @Input() set showHopelessBefore(show: Date) {
+        this._showHopelessBefore = show;
+        if (this.initDone) { // reload on update
+            this.holdsGrid.reload();
+        }
+    }
+
     constructor(
         private net: NetService,
         private org: OrgService,
         private store: ServerStoreService,
         private auth: AuthService,
-        private printer: PrintService
+        private printer: PrintService,
+        private holdings: HoldingsService
     ) {
         this.gridDataSource = new GridDataSource();
         this.enablePreFetch = null;
@@ -186,6 +214,7 @@ export class HoldsGridComponent implements OnInit {
     }
 
     applyFilters(): any {
+
         const filters: any = {
             is_staff_request: true,
             fulfillment_time: this._showFulfilledSince ?
@@ -194,6 +223,27 @@ export class HoldsGridComponent implements OnInit {
                 this._showCanceledSince.toISOString() : null,
         };
 
+        if (this.hopeless) {
+          filters['hopeless_holds'] = {
+            'start_date' : this._showHopelessAfter
+              ? (
+                  // FIXME -- consistency desired, string or object
+                  typeof this._showHopelessAfter === 'object'
+                  ? this._showHopelessAfter.toISOString()
+                  : this._showHopelessAfter
+                )
+              : '1970-01-01T00:00:00.000Z',
+            'end_date' : this._showHopelessBefore
+              ? (
+                  // FIXME -- consistency desired, string or object
+                  typeof this._showHopelessBefore === 'object'
+                  ? this._showHopelessBefore.toISOString()
+                  : this._showHopelessBefore
+                )
+              : (new Date()).toISOString()
+          };
+        }
+
         if (this.pickupLib) {
             filters.pickup_lib =
                 this.org.descendants(this.pickupLib, true);
@@ -270,6 +320,16 @@ export class HoldsGridComponent implements OnInit {
         return observable;
     }
 
+    metaRecordHoldsSelected(rows: IdlObject[]) {
+        var found = false;
+        rows.forEach( row => {
+           if (row.hold_type == 'M') {
+             found = true;
+           }
+        });
+        return found;
+    }
+
     showDetails(rows: any[]) {
         this.showDetail(rows[0]);
     }
@@ -315,6 +375,35 @@ export class HoldsGridComponent implements OnInit {
         }
     }
 
+    showOrder(rows: any[]) {
+        //Doesn't work in Typescript currently without compiler option:
+        //   const bibIds = [...new Set( rows.map(r => r.record_id) )];
+        const bibIds = Array.from(
+          new Set( rows.filter(r => r.hold_type!='M').map(r => r.record_id) ));
+        bibIds.forEach( bibId => {
+          const url =
+              '/eg/staff/acq/legacy/lineitem/related/' + bibId + '?target=bib';
+          window.open(url, '_blank');
+        });
+    }
+
+    addVolume(rows: any[]) {
+        const bibIds = Array.from(
+          new Set( rows.filter(r => r.hold_type!='M').map(r => r.record_id) ));
+        bibIds.forEach( bibId => {
+          this.holdings.spawnAddHoldingsUi(bibId);
+        });
+    }
+
+    showTitle(rows: any[]) {
+        const bibIds = Array.from(new Set( rows.map(r => r.record_id) ));
+        bibIds.forEach( bibId => {
+          //const url = '/eg/staff/cat/catalog/record/' + bibId;
+          const url = '/eg2/staff/catalog/record/' + bibId;
+          window.open(url, '_blank');
+        });
+    }
+
     showManageDialog(rows: any[]) {
         const holdIds = rows.map(r => r.id).filter(id => Boolean(id));
         if (holdIds.length > 0) {
index 7cf6e26..c72a55e 100644 (file)
@@ -2142,6 +2142,9 @@ sub wide_hold_data {
     my $last_captured_hold = delete($$restrictions{last_captured_hold}) || 'false';
     $last_captured_hold = $last_captured_hold eq 'true' ? 1 : 0;
 
+    # option to filter for hopeless holds by date range
+    my $hopeless_holds = delete($$restrictions{hopeless_holds}) || 'false';
+
     my $initial_condition = 'TRUE';
     if ($last_captured_hold) {
         $initial_condition = <<"        SQL";
@@ -2156,6 +2159,13 @@ sub wide_hold_data {
         SQL
     }
 
+    if (ref($hopeless_holds) =~ /HASH/ && $$hopeless_holds{start_date} && $$hopeless_holds{end_date}) {
+        my $start_date = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($$hopeless_holds{start_date}));
+        my $end_date = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($$hopeless_holds{end_date}));
+        my $hopeless_condition = "(frozen IS FALSE AND h.hopeless_date >= '$start_date' AND h.hopeless_date <= '$end_date')";
+        $initial_condition .= " AND $hopeless_condition";
+    }
+
     my $select = <<"    SQL";
 WITH
     t_field AS (SELECT field FROM config.display_field_map WHERE name = 'title'),
index 4d01c4a..e536301 100644 (file)
@@ -23,6 +23,7 @@
     ,[ l('Group Penalty Thresholds'), "./admin/local/permission/grp_penalty_threshold" ]
     ,[ l('Hold Policies'), "./admin/local/config/hold_matrix_matchpoint" ]
     ,[ l('Holdings Template Editor'), "./cat/volcopy/edit_templates" ]
+    ,[ l('Hopeless Holds'), "/eg2/staff/hopeless" ]
     ,[ l('Item Alert Suppression'), "./admin/local/actor/copy_alert_suppress" ]
     ,[ l('Item Alert Types'), "./admin/local/config/copy_alert_types" ]
     ,[ l('Item Tags'), "./admin/local/asset/copy_tag" ]