From e8fe50530ba03af36655564123fe3230a40ca483 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Wed, 29 Apr 2020 17:27:23 -0400 Subject: [PATCH] LP1847800 Admin grids support config_field links For IDL fields which have config_field=true, the value in the admin grid is rendered as a link to the grid for the linked field. Additionally, the link contains a gridFilter so the destination grid only displays rows related to the selected field. Adds config_field=true values for z39.50 source and two links for hard due date and hard due date values. Signed-off-by: Bill Erickson Signed-off-by: Chris Sharp --- Open-ILS/examples/fm_IDL.xml | 7 +- .../staff/admin/basic-admin-page.component.ts | 4 + .../admin/local/admin-carousel.component.ts | 6 +- .../floating-group.component.ts | 6 +- .../admin-page/admin-page.component.html | 9 ++ .../share/admin-page/admin-page.component.ts | 100 +++++++++++++++++- 6 files changed, 125 insertions(+), 7 deletions(-) diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index c414118d95..9e91709b59 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -1252,7 +1252,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -3550,9 +3550,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + @@ -3567,7 +3570,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts index ceed28792d..1c9b4c80a3 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts @@ -13,6 +13,7 @@ import {IdlService} from '@eg/core/idl.service'; ` @@ -24,6 +25,7 @@ export class BasicAdminPageComponent implements OnInit { classLabel: string; persistKeyPfx: string; readonlyFields = ''; + configLinkBasePath = '/staff/admin'; // Tell the admin page to disable and hide the automagic org unit filter disableOrgFilter: boolean; @@ -59,6 +61,8 @@ export class BasicAdminPageComponent implements OnInit { // ACQ is a special case, because unlike 'server', 'local', // 'workstation', the schema ('acq') is the root of the path. this.persistKeyPfx = ''; + } else { + this.configLinkBasePath += '/' + this.persistKeyPfx; } // Pass the readonlyFields param if available diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-carousel.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-carousel.component.ts index ab496ec032..3d048d6a6b 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-carousel.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-carousel.component.ts @@ -1,4 +1,6 @@ import {Component, Input, ViewChild, OnInit} from '@angular/core'; +import {Location} from '@angular/common'; +import {FormatService} from '@eg/core/format.service'; import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.component'; import {ActivatedRoute} from '@angular/router'; import {IdlService, IdlObject} from '@eg/core/idl.service'; @@ -28,6 +30,8 @@ export class AdminCarouselComponent extends AdminPageComponent implements OnInit constructor( route: ActivatedRoute, + ngLocation: Location, + format: FormatService, idl: IdlService, org: OrgService, auth: AuthService, @@ -36,7 +40,7 @@ export class AdminCarouselComponent extends AdminPageComponent implements OnInit toast: ToastService, private net: NetService ) { - super(route, idl, org, auth, pcrud, perm, toast); + super(route, ngLocation, format, idl, org, auth, pcrud, perm, toast); } ngOnInit() { diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/floating-group/floating-group.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/floating-group/floating-group.component.ts index da84179689..c9f452901e 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/floating-group/floating-group.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/floating-group/floating-group.component.ts @@ -1,6 +1,8 @@ import {Pager} from '@eg/share/util/pager'; import {Component, Input, ViewChild, OnInit} from '@angular/core'; +import {Location} from '@angular/common'; import {Router, ActivatedRoute} from '@angular/router'; +import {FormatService} from '@eg/core/format.service'; import {IdlService, IdlObject} from '@eg/core/idl.service'; import {GridDataSource} from '@eg/share/grid/grid'; import {GridComponent} from '@eg/share/grid/grid.component'; @@ -25,6 +27,8 @@ export class FloatingGroupComponent extends AdminPageComponent implements OnInit constructor( route: ActivatedRoute, + ngLocation: Location, + format: FormatService, idl: IdlService, org: OrgService, auth: AuthService, @@ -33,7 +37,7 @@ export class FloatingGroupComponent extends AdminPageComponent implements OnInit toast: ToastService, private router: Router ) { - super(route, idl, org, auth, pcrud, perm, toast); + super(route, ngLocation, format, idl, org, auth, pcrud, perm, toast); } ngOnInit() { diff --git a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html index e0ae6c3b03..c05a98b216 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html +++ b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html @@ -33,6 +33,11 @@ + + {{configLinkLabel(row, col)}} + + + + + + f.datatype === 'link'); this.configFields = this.idlClassDef.fields.filter(f => f.config_field); @@ -244,7 +259,10 @@ export class AdminPageComponent implements OnInit { const search: any = {}; - search[this.orgField] = this.searchOrgs.orgIds || [this.contextOrg.id()]; + if (this.orgField) { + search[this.orgField] = + this.searchOrgs.orgIds || [this.contextOrg.id()]; + } if (this.gridFilters) { // Lay the URL grid filters over our search object. @@ -370,6 +388,82 @@ export class AdminPageComponent implements OnInit { this.translator.open({size: 'lg'}); } + + // Construct a routerLink path for a configField. + configFieldRouteLink(row: any, col: GridColumn): string { + const cf = this.configFields.filter(field => field.name === col.name)[0]; + const linkClass = this.idl.classes[cf['class']]; + const pathParts = linkClass.table.split(/\./); // schema.tablename + return `${this.configLinkBasePath}/${pathParts[0]}/${pathParts[1]}`; + } + + // Compiles a gridFilter value used when navigating to a linked + // class via configField. The filter ensures the linked page + // only shows rows which refer back to the object from which the + // link was clicked. + configFieldRouteParams(row: any, col: GridColumn): any { + const cf = this.configFields.filter(field => field.name === col.name)[0]; + let value = this.configFieldLinkedValue(row, col); + + // For certain has-a relationships, the linked object will be + // fleshed so its display (selector) value can be used. + // Extract the scalar value found at the remote target field. + if (value && typeof value === 'object') { value = value[cf.key](); } + + const filter: any = {}; + filter[cf.key] = value; + + return {gridFilters : JSON.stringify(filter)}; + } + + // Returns the value on the local object for the field which + // refers to the remote object. This may be a scalar or a + // fleshed IDL object. + configFieldLinkedValue(row: any, col: GridColumn): any { + const cf = this.configFields.filter(field => field.name === col.name)[0]; + const linkClass = this.idl.classes[cf['class']]; + + // cf.key is the name of the field on the linked object that matches + // the value on our local object. + // In as has_many relationship, the remote field has its own + // 'key' value which determines which field on the local object + // represents the other end of the relationship. This is + // typically, but not always the local pkey field. + + const localField = + cf.reltype === 'has_many' ? + (linkClass.field_map[cf.key].key || this.pkeyField) : cf.name; + + return row[localField](); + } + + // Returns a URL suitable for using as an href. + // We use an href to jump to the secondary admin page because + // routerLink within the same base component results in component + // reuse of a series of components which were not designed with + // reuse in mind. + configFieldLinkUrl(row: any, col: GridColumn): string { + const path = this.configFieldRouteLink(row, col); + const filters = this.configFieldRouteParams(row, col); + const url = path + '?gridFilters=' + + encodeURIComponent(filters.gridFilters); + + return this.ngLocation.prepareExternalUrl(url); + } + + configLinkLabel(row: any, col: GridColumn): string { + const cf = this.configFields.filter(field => field.name === col.name)[0]; + + // Has-many links have no specific value to use for display + // so just use the column label. + if (cf.reltype === 'has_many') { return col.label; } + + return this.format.transform({ + value: row[col.name](), + idlClass: this.idlClass, + idlField: col.name + }); + } } -- 2.43.2