LP1879335 Manage Authorities Angular WIP
authorBill Erickson <berickxx@gmail.com>
Mon, 18 May 2020 16:49:21 +0000 (12:49 -0400)
committerBill Erickson <berickxx@gmail.com>
Mon, 18 May 2020 16:49:21 +0000 (12:49 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/staff/cat/authority/authority.module.ts
Open-ILS/src/eg2/src/app/staff/cat/authority/browse.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/cat/authority/browse.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/cat/authority/manage.component.html
Open-ILS/src/eg2/src/app/staff/cat/authority/manage.component.ts
Open-ILS/src/eg2/src/app/staff/cat/authority/routing.module.ts
Open-ILS/src/eg2/src/app/staff/share/bib-list/bib-list.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/bib-list/bib-list.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/bib-list/bib-list.module.ts [new file with mode: 0644]

index c6b8d6e..ef2acbe 100644 (file)
@@ -4,18 +4,22 @@ import {CommonWidgetsModule} from '@eg/share/common-widgets.module';
 import {AuthorityRoutingModule} from './routing.module';
 import {MarcEditModule} from '@eg/staff/share/marc-edit/marc-edit.module';
 import {AuthorityMarcEditComponent} from './marc-edit.component';
+import {BrowseAuthorityComponent} from './browse.component';
 import {ManageAuthorityComponent} from './manage.component';
+import {BibListModule} from '@eg/staff/share/bib-list/bib-list.module';
 
 @NgModule({
   declarations: [
     AuthorityMarcEditComponent,
+    BrowseAuthorityComponent,
     ManageAuthorityComponent
   ],
   imports: [
     StaffCommonModule,
     CommonWidgetsModule,
     MarcEditModule,
-    AuthorityRoutingModule
+    AuthorityRoutingModule,
+    BibListModule
   ],
   providers: [
   ]
diff --git a/Open-ILS/src/eg2/src/app/staff/cat/authority/browse.component.html b/Open-ILS/src/eg2/src/app/staff/cat/authority/browse.component.html
new file mode 100644 (file)
index 0000000..6c0794a
--- /dev/null
@@ -0,0 +1,69 @@
+<eg-staff-banner bannerText="Manage Authority Records" i18n-bannerText>
+</eg-staff-banner>
+
+<div class="row form-inline mb-3">
+  <div class="col-lg-3">
+    <div class="input-group">
+      <div class="input-group-prepend">
+        <span class="input-group-text" id="search-term" i18n>Search Term</span>
+      </div>
+      <input type="text" class="form-control" placeholder="Search Term" 
+        i18n-placeholder aria-describedby="search-term" 
+        (keyup.enter)="search()" [(ngModel)]="searchTerm">
+    </div>
+  </div>
+  <div class="col-lg-5">
+    <div class="input-group">
+      <div class="input-group-prepend">
+        <span class="input-group-text" id="auth-axis" i18n>Authority Type</span>
+      </div>
+      <eg-combobox #axisCbox [(ngModel)]="authorityAxis" 
+        [entries]="authorityAxes" (onChange)="search()">
+      </eg-combobox>
+      <button class="btn btn-outline-dark ml-2" (click)="search()" i18n>Submit</button>
+    </div>
+  </div>
+  <div class="col-lg-4 d-flex">
+    <div class="flex-1"></div><!-- push right -->
+    <div class="form-inline">
+      <button class="btn btn-outline-dark ml-2" (click)="search(-1)" i18n>Previous</button>
+      <label for='offset-input' class="form-control ml-2" i18n>Page</label>
+      <input class="form-control" type="number" 
+        [(ngModel)]="searchOffset" id="offset-input" (change)="search()"/>
+      <button class="btn btn-outline-dark ml-2" (click)="search(1)" i18n>Next</button>
+    </div>
+  </div>
+</div>
+
+<ng-template #headingTemplate let-row="row">
+  <a routerLink="/staff/cat/authority/manage/{{row.authority.id()}}/bibs"
+    i18n-title title="Manage Authority {{row.authority.id()}}">
+    {{row.heading}}
+  </a>
+</ng-template>
+
+<eg-grid #grid [dataSource]="dataSource" [disablePaging]="true"
+  [cellTextGenerator]="cellTextGenerator" persistKey="cat.authority.browse">
+  <eg-grid-column name="id" label="ID" path="authority.id" i18n-label 
+    [index]="true" flex="1"></eg-grid-column>
+  <eg-grid-column name="link_count" label="Linked Bibs" 
+    i18n-label flex="1"></eg-grid-column>
+  <eg-grid-column name="heading" label="Heading" i18n-label flex="3"
+    [cellTemplate]="headingTemplate"></eg-grid-column>
+  <eg-grid-column name="control_set" path="authority.control_set.name" 
+    label="Control Set" i18n-label flex="1"></eg-grid-column>
+  <eg-grid-column name="thesaurus" label="Thesaurus" i18n-label flex="1"></eg-grid-column>
+  <eg-grid-column name="thesaurus_code" label="Thesaurus Code" 
+    i18n-label flex="1"></eg-grid-column>
+  <eg-grid-column name="creator" label="Creator" i18n-label
+    path="authority.creator.usrname" flex="1"></eg-grid-column>
+  <eg-grid-column name="create_date" label="Create Date" i18n-label
+    path="authority.create_date" flex="1" datatype="timestamp"></eg-grid-column>
+  <eg-grid-column name="edit_date" label="Edit Date" i18n-label
+    path="authority.edit_date" flex="1" datatype="timestamp"></eg-grid-column>
+  <eg-grid-column name="source" label="Source" i18n-label
+    path="authority.source" flex="1"></eg-grid-column>
+  <eg-grid-column name="owner" label="Owner" i18n-label
+    path="authority.owner" flex="1"></eg-grid-column>
+</eg-grid>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/cat/authority/browse.component.ts b/Open-ILS/src/eg2/src/app/staff/cat/authority/browse.component.ts
new file mode 100644 (file)
index 0000000..0724231
--- /dev/null
@@ -0,0 +1,102 @@
+import {Component, OnInit, ViewChild} from '@angular/core';
+import {Observable, empty} from 'rxjs';
+import {map, switchMap} from 'rxjs/operators';
+import {IdlObject} from '@eg/core/idl.service';
+import {Pager} from '@eg/share/util/pager';
+import {NetService} from '@eg/core/net.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {OrgService} from '@eg/core/org.service';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridContext, GridDataSource, GridCellTextGenerator} from '@eg/share/grid/grid';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+
+/* Find, merge, and edit authority records */
+
+@Component({
+  templateUrl: 'browse.component.html',
+  styles: [`#offset-input { width: 4em; }`]
+})
+export class BrowseAuthorityComponent implements OnInit {
+
+    // Grid paging is disabled in this UI to support browsing in
+    // both directions.  Define our own paging trackers.
+    pageSize = 15;
+    searchOffset = 0;
+
+    searchTerm: string;
+    authorityAxis: ComboboxEntry;
+    authorityAxes: ComboboxEntry[];
+    dataSource: GridDataSource;
+    cellTextGenerator: GridCellTextGenerator;
+
+    @ViewChild('grid', {static: false}) grid: GridComponent;
+
+    constructor(
+        private net: NetService,
+        private org: OrgService,
+        private pcrud: PcrudService
+    ) {
+    }
+
+    ngOnInit() {
+
+        this.pcrud.retrieveAll('aba', {}, {atomic: true})
+        .subscribe(axes => {
+                this.authorityAxes = axes
+                    .map(axis => ({id: axis.code(), label: axis.name()}))
+                    .sort((a1, a2) => a1.label < a2.label ? -1 : 1);
+        });
+
+        this.dataSource = new GridDataSource();
+
+        this.dataSource.getRows = (pager: Pager, sort: any): Observable<any> => {
+            return this.loadAuthorities();
+        }
+
+        this.cellTextGenerator = {
+            heading: row => row.heading
+        }
+    }
+
+    loadAuthorities(): Observable<any> {
+
+        if (!this.searchTerm || !this.authorityAxis.id) {
+            return empty();
+        }
+
+        return this.net.request(
+            'open-ils.supercat',
+            'open-ils.supercat.authority.browse.by_axis',
+            this.authorityAxis.id, this.searchTerm,
+            this.pageSize, this.searchOffset
+
+        ).pipe(switchMap(authIds => {
+
+            return this.net.request(
+                'open-ils.search',
+                'open-ils.search.authority.main_entry', authIds
+            );
+
+        })).pipe(map(authMeta => {
+
+            const oOrg = this.org.get(authMeta.authority.owner());
+
+            return {
+                authority: authMeta.authority,
+                link_count: authMeta.linked_bibs.length,
+                heading: authMeta.heading,
+                thesaurus: authMeta.thesaurus,
+                thesaurus_code: authMeta.thesaurus_code,
+                owner: oOrg ? oOrg.shortname() : ''
+            };
+        }));
+    }
+
+    search(offset?: number) {
+        if (offset) { this.searchOffset += offset; }
+
+        this.grid.reload();
+    }
+}
+
+
index bf619be..6fa633f 100644 (file)
@@ -1,62 +1,23 @@
-<eg-staff-banner bannerText="Manage Authority Records" i18n-bannerText>
+<eg-staff-banner bannerText="Manage Authority Record #{{authId}}" i18n-bannerText>
 </eg-staff-banner>
 
-<div class="row form-inline mb-3">
-  <div class="col-lg-3">
-    <div class="input-group">
-      <div class="input-group-prepend">
-        <span class="input-group-text" id="search-term" i18n>Search Term</span>
+<ngb-tabset #authTabs [activeId]="authTab" 
+  (tabChange)="beforeTabChange($event)">
+  <ngb-tab title="Linked Bibs" i18n-title id="bibs">
+    <ng-template ngbTabContent>
+      <div class="mt-3" *ngIf="authMeta">
+        <eg-bib-list #bibList [bibIds]="authMeta.linked_bibs"></eg-bib-list>
       </div>
-      <input type="text" class="form-control" placeholder="Search Term" 
-        i18n-placeholder aria-describedby="search-term" 
-        (keyup.enter)="search()" [(ngModel)]="searchTerm">
-    </div>
-  </div>
-  <div class="col-lg-5">
-    <div class="input-group">
-      <div class="input-group-prepend">
-        <span class="input-group-text" id="auth-axis" i18n>Authority Type</span>
+    </ng-template>
+  </ngb-tab>
+  <ngb-tab title="Edit" i18n-title id="edit">
+    <ng-template ngbTabContent>
+      <div class="mt-3">
+        <eg-marc-editor #marcEditor recordType="authority" [recordId]="authId">
+        </eg-marc-editor>
       </div>
-      <eg-combobox #axisCbox [(ngModel)]="authorityAxis" 
-        [entries]="authorityAxes" (onChange)="search()">
-      </eg-combobox>
-      <button class="btn btn-outline-dark ml-2" (click)="search()" i18n>Submit</button>
-    </div>
-  </div>
-  <div class="col-lg-4 d-flex">
-    <div class="flex-1"></div><!-- push right -->
-    <div class="form-inline">
-      <button class="btn btn-outline-dark ml-2" (click)="search(-1)" i18n>Previous</button>
-      <label for='offset-input' class="form-control ml-2" i18n>Page</label>
-      <input class="form-control" type="number" 
-        [(ngModel)]="searchOffset" id="offset-input" (change)="search()"/>
-      <button class="btn btn-outline-dark ml-2" (click)="search(1)" i18n>Next</button>
-    </div>
-  </div>
-</div>
+    </ng-template>
+  </ngb-tab>
+</ngb-tabset>
 
-<eg-grid #grid [dataSource]="dataSource" [disablePaging]="true">
-  <eg-grid-column name="id" label="ID" path="authority.id" i18n-label 
-    [index]="true" flex="1"></eg-grid-column>
-  <eg-grid-column name="link_count" label="Linked Bibs" 
-    i18n-label flex="1"></eg-grid-column>
-  <eg-grid-column name="heading" label="Heading" i18n-label flex="3"></eg-grid-column>
-  <eg-grid-column name="control_set" path="authority.control_set.name" 
-    label="Control Set" i18n-label flex="1"></eg-grid-column>
-  <eg-grid-column name="thesaurus" label="Thesaurus" i18n-label flex="1"></eg-grid-column>
-  <eg-grid-column name="thesaurus_code" label="Thesaurus Code" 
-    i18n-label flex="1"></eg-grid-column>
-  <eg-grid-column name="creator" label="Creator" i18n-label
-    path="authority.creator.usrname" flex="1"></eg-grid-column>
-  <eg-grid-column name="create_date" label="Create Date" i18n-label
-    path="authority.create_date" flex="1" datatype="timestamp"></eg-grid-column>
-  <eg-grid-column name="edit_date" label="Edit Date" i18n-label
-    path="authority.edit_date" flex="1" datatype="timestamp"></eg-grid-column>
-  <eg-grid-column name="source" label="Source" i18n-label
-    path="authority.source" flex="1"></eg-grid-column>
-  <eg-grid-column name="owner" label="Owner" i18n-label
-    path="authority.owner" flex="1"></eg-grid-column>
-
-
-</eg-grid>
 
index 661857d..7ddf38a 100644 (file)
@@ -1,6 +1,8 @@
 import {Component, OnInit, ViewChild} from '@angular/core';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
 import {Observable, empty} from 'rxjs';
 import {map, switchMap} from 'rxjs/operators';
+import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap';
 import {IdlObject} from '@eg/core/idl.service';
 import {Pager} from '@eg/share/util/pager';
 import {NetService} from '@eg/core/net.service';
@@ -13,24 +15,20 @@ import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
 /* Find, merge, and edit authority records */
 
 @Component({
-  templateUrl: 'manage.component.html',
-  styles: [`#offset-input { width: 4em; }`]
+  templateUrl: 'manage.component.html'
 })
 export class ManageAuthorityComponent implements OnInit {
 
-    // Grid paging is disabled in this UI to support browsing in
-    // both directions.  Define our own paging trackers.
-    pageSize = 15;
-    searchOffset = 0;
+    authId: number;
+    authTab = 'bibs';
+    authMeta: any;
 
-    searchTerm: string;
-    authorityAxis: ComboboxEntry;
-    authorityAxes: ComboboxEntry[];
-    dataSource: GridDataSource;
-
-    @ViewChild('grid', {static: false}) grid: GridComponent;
+    bibsDataSource: GridDataSource;
+    @ViewChild('bibsGrid', {static: false}) bibsGrid: GridComponent;
 
     constructor(
+        private router: Router,
+        private route: ActivatedRoute,
         private net: NetService,
         private org: OrgService,
         private pcrud: PcrudService
@@ -39,58 +37,48 @@ export class ManageAuthorityComponent implements OnInit {
 
     ngOnInit() {
 
-        this.pcrud.retrieveAll('aba', {}, {atomic: true})
-        .subscribe(axes => {
-                this.authorityAxes = axes
-                    .map(axis => ({id: axis.code(), label: axis.name()}))
-                    .sort((a1, a2) => a1.label < a2.label ? -1 : 1);
-        });
-
-        this.dataSource = new GridDataSource();
+        this.bibsDataSource = new GridDataSource();
 
-        this.dataSource.getRows = (pager: Pager, sort: any): Observable<any> => {
-            return this.loadAuthorities();
+        this.bibsDataSource.getRows = (pager: Pager, sort: any): Observable<any> => {
+            return this.loadLinkedBibs();
         }
-    }
-
-    loadAuthorities(): Observable<any> {
 
-        if (!this.searchTerm || !this.authorityAxis.id) {
-            return empty();
-        }
+        this.route.paramMap.subscribe((params: ParamMap) => {
+            this.authTab = params.get('tab') || 'bibs';
+            const id = +params.get('id');
 
-        return this.net.request(
-            'open-ils.supercat',
-            'open-ils.supercat.authority.browse.by_axis',
-            this.authorityAxis.id, this.searchTerm,
-            this.pageSize, this.searchOffset
+            if (id !== this.authId) {
+                this.authId = id;
 
-        ).pipe(switchMap(authIds => {
+                this.net.request(
+                    'open-ils.search',
+                    'open-ils.search.authority.main_entry', this.authId
+                ).subscribe(meta => this.authMeta = meta);
+            }
+        });
+    }
 
-            return this.net.request(
-                'open-ils.search',
-                'open-ils.search.authority.main_entry', authIds
-            );
+    // Changing a tab in the UI means changing the route.
+    // Changing the route ultimately results in changing the tab.
+    beforeTabChange(evt: NgbTabChangeEvent) {
 
-        })).pipe(map(authMeta => {
+        // prevent tab changing until after route navigation
+        evt.preventDefault();
 
-            const oOrg = this.org.get(authMeta.authority.owner());
+        this.authTab = evt.nextId;
+        this.routeToTab();
+    }
 
-            return {
-                authority: authMeta.authority,
-                link_count: authMeta.linked_bibs.length,
-                heading: authMeta.heading,
-                thesaurus: authMeta.thesaurus,
-                thesaurus_code: authMeta.thesaurus_code,
-                owner: oOrg ? oOrg.shortname() : ''
-            };
-        }));
+    routeToTab() {
+        const url =
+            `/staff/cat/authority/manage/${this.authId}/${this.authTab}`;
+        this.router.navigate([url]);
     }
 
-    search(offset?: number) {
-        if (offset) { this.searchOffset += offset; }
 
-        this.grid.reload();
+    loadLinkedBibs(): Observable<any> {
+
+        return empty();
     }
 }
 
index cd688a8..b3fcf36 100644 (file)
@@ -1,6 +1,7 @@
 import {NgModule} from '@angular/core';
 import {RouterModule, Routes} from '@angular/router';
 import {AuthorityMarcEditComponent} from './marc-edit.component';
+import {BrowseAuthorityComponent} from './browse.component';
 import {ManageAuthorityComponent} from './manage.component';
 
 const routes: Routes = [{
@@ -10,7 +11,13 @@ const routes: Routes = [{
     path: 'edit/:id',
     component: AuthorityMarcEditComponent
   }, {
-    path: 'manage',
+    path: 'browse',
+    component: BrowseAuthorityComponent
+  }, {
+    path: 'manage/:id/:tab',
+    component: ManageAuthorityComponent
+  }, {
+    path: 'manage/:id/:tab',
     component: ManageAuthorityComponent
 }];
 
diff --git a/Open-ILS/src/eg2/src/app/staff/share/bib-list/bib-list.component.html b/Open-ILS/src/eg2/src/app/staff/share/bib-list/bib-list.component.html
new file mode 100644 (file)
index 0000000..df01a5c
--- /dev/null
@@ -0,0 +1,16 @@
+<ng-template #titleTemplate let-row="row">
+  <a routerLink="/staff/catalog/record/{{row.id()}}"
+    i18n-title title="View Record {{row.id()}}">
+    {{row.title()}}
+  </a>
+</ng-template>
+
+<eg-grid #grid [dataSource]="dataSource" idlClass="rmsr" [sortable]="true"
+  [cellTextGenerator]="cellTextGenerator" [persistKey]="gridPersistKey">
+
+  <eg-grid-column name="title" [cellTemplate]="titleTemplate"
+    label="Title" i18n-label></eg-grid-column>
+
+  <eg-grid-column name="author" label="Author" i18n-label></eg-grid-column>
+
+</eg-grid>
diff --git a/Open-ILS/src/eg2/src/app/staff/share/bib-list/bib-list.component.ts b/Open-ILS/src/eg2/src/app/staff/share/bib-list/bib-list.component.ts
new file mode 100644 (file)
index 0000000..84d8a37
--- /dev/null
@@ -0,0 +1,75 @@
+import {Component, Input, OnInit, ViewChild} from '@angular/core';
+import {Observable, empty} from 'rxjs';
+import {map, switchMap} from 'rxjs/operators';
+import {IdlObject} from '@eg/core/idl.service';
+import {Pager} from '@eg/share/util/pager';
+import {NetService} from '@eg/core/net.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {OrgService} from '@eg/core/org.service';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridContext, GridDataSource, GridCellTextGenerator} from '@eg/share/grid/grid';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+
+
+/* List of bib records and associated actions */
+
+@Component({
+  templateUrl: 'bib-list.component.html',
+  selector: 'eg-bib-list'
+})
+export class BibListComponent implements OnInit {
+
+    // Display bibs linked to this authority record.
+    @Input() bibIds: number[];
+
+    @Input() bucketId: number; // TODO
+
+    @Input() gridPersistKey: string;
+
+    dataSource: GridDataSource;
+    cellTextGenerator: GridCellTextGenerator;
+
+    @ViewChild('grid', {static: false}) grid: GridComponent;
+
+    constructor(
+        private net: NetService,
+        private org: OrgService,
+        private pcrud: PcrudService
+    ) {
+    }
+
+    ngOnInit() {
+        this.dataSource = new GridDataSource();
+
+        this.dataSource.getRows = (pager: Pager, sort: any): Observable<any> => {
+
+            if (this.bibIds) {
+                return this.loadIds(pager, sort);
+            }
+
+            return empty();
+        }
+
+        this.cellTextGenerator = {
+        };
+    }
+
+    loadIds(pager: Pager, sort: any): Observable<any> {
+        if (this.bibIds.length === 0) {
+            return empty();
+        }
+
+        return this.pcrud.search('rmsr', {id: this.bibIds}, {
+            order_by: {}, /* todo */
+            limit: pager.limit,
+            offset: pager.offset,
+            flesh: 2,
+            flesh_fields: {
+                rmsr: ['bib_record'],
+                bre: ['creator', 'editor']
+            }
+        });
+    }
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/bib-list/bib-list.module.ts b/Open-ILS/src/eg2/src/app/staff/share/bib-list/bib-list.module.ts
new file mode 100644 (file)
index 0000000..3767156
--- /dev/null
@@ -0,0 +1,20 @@
+import {NgModule} from '@angular/core';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {BibListComponent} from './bib-list.component';
+
+@NgModule({
+    declarations: [
+      BibListComponent
+    ],
+    imports: [
+        StaffCommonModule
+    ],
+    exports: [
+      BibListComponent
+    ],
+    providers: [
+    ]
+})
+
+export class BibListModule {}
+