lp1857911 angularized stat cat admin interfaces
authorJason Etheridge <jason@EquinoxOLI.org>
Wed, 14 Dec 2022 05:57:25 +0000 (00:57 -0500)
committerJane Sandberg <js7389@princeton.edu>
Tue, 2 May 2023 21:12:26 +0000 (14:12 -0700)
* for stat cats and stat cat entries in the IDL, make sure we have pcrud controller enabled and pcrud permissions set.  Also set config_field for "entries" so that the AdminPage component can link them out to their own admin pages
* item and patron stat cat admin pages
* lp1857911 release notes

Leverages the BasicAdminPage component, with some tweaks.

Changes to the BasicAdminPage and AdminPage components all default to off, so should not affect existing interfaces:

* add an option to hide the Clear Filters action from subordinate admin pages (when you follow config fields)
* add an option to propagate recordLabel to the fmEditor component, and also use said label within the admin page
* add an option to propagate orgDefaultAllowed to the fmEditor component

For the stat cat and entry admin pages specifically, we leverage these to:

* hide Clear Filters so that we don't let the user get into a situation where they try to create a new stat cat entry but have a blank and unsettable stat cat field (because we mark that field as read-only in the fmEditor)
* default the Owner field for new stat cats and entries to the workstation library
* use Item and Patron instead of Asset and Actor in various labels (at least until we're ready to make that change wholesale in the IDL)

Signed-off-by: Jason Etheridge <jason@EquinoxOLI.org>
Signed-off-by: Jane Sandberg <js7389@princeton.edu>
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.spec.ts
Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts
Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts
Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html
Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts
docs/RELEASE_NOTES_NEXT/miscellaneous.adoc

index 8bccb81..949ed2b 100644 (file)
@@ -7401,7 +7401,7 @@ SELECT  usr,
        </class>
        <class id="asc" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::stat_cat" oils_persist:tablename="asset.stat_cat" reporter:label="Asset Statistical Category">
                <fields oils_persist:primary="id" oils_persist:sequence="asset.stat_cat_id_seq">
-                       <field reporter:label="Entries" name="entries" oils_persist:virtual="true" reporter:datatype="link"/>
+                       <field reporter:label="Entries" name="entries" oils_persist:virtual="true" reporter:datatype="link" config_field="true"/>
                        <field reporter:label="Stat Cat ID" name="id" reporter:datatype="id" reporter:selector="name"/>
                        <field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true"/>
                        <field reporter:label="OPAC Visible" name="opac_visible" reporter:datatype="bool"/>
@@ -7418,8 +7418,12 @@ SELECT  usr,
                </links>
                <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
                        <actions>
+                <create permission="CREATE_COPY_STAT_CAT" context_field="owner"/>
                                <retrieve permission="STAFF_LOGIN" global_required="true"/>
-                       </actions>
+                <retrieve />
+                <update permission="UPDATE_COPY_STAT_CAT" context_field="owner"/>
+                <delete permission="DELETE_COPY_STAT_CAT" context_field="owner"/>
+            </actions>
                </permacrud>
        </class>
        <class id="ac" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::card" oils_persist:tablename="actor.card" reporter:label="Library Card">
@@ -7458,7 +7462,7 @@ SELECT  usr,
     </class>
        <class id="actsc" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::stat_cat" oils_persist:tablename="actor.stat_cat" reporter:label="User Statistical Category">
                <fields oils_persist:primary="id" oils_persist:sequence="actor.stat_cat_id_seq">
-                       <field reporter:label="Entries" name="entries" oils_persist:virtual="true" reporter:datatype="link"/>
+                       <field reporter:label="Entries" name="entries" oils_persist:virtual="true" reporter:datatype="link" config_field="true"/>
                        <field reporter:label="Default Entries" name="default_entries" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Stat Cat ID" name="id" reporter:datatype="id" reporter:selector="name"/>
                        <field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true"/>
@@ -7749,7 +7753,7 @@ SELECT  usr,
                        </actions>
                </permacrud>
        </class>
-       <class id="actsce" controller="open-ils.cstore" oils_obj:fieldmapper="actor::stat_cat_entry" oils_persist:tablename="actor.stat_cat_entry" reporter:label="User Stat Cat Entry">
+       <class id="actsce" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::stat_cat_entry" oils_persist:tablename="actor.stat_cat_entry" reporter:label="User Stat Cat Entry">
                <fields oils_persist:primary="id" oils_persist:sequence="actor.stat_cat_entry_id_seq">
                        <field reporter:label="Entry ID" name="id" reporter:datatype="id" />
                        <field reporter:label="Entry Owner" name="owner" reporter:datatype="link"/>
@@ -7762,6 +7766,20 @@ SELECT  usr,
                        <link field="owner" reltype="has_a" key="id" map="" class="aou"/>
                        <link field="default_entries" reltype="has_many" key="stat_cat_entry" map="" class="actsced"/>
                </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                <create permission="CREATE_PATRON_STAT_CAT_ENTRY">
+                    <context link="stat_cat" field="owner"/>
+                </create>
+                               <retrieve permission="STAFF_LOGIN" global_required="true"/>
+                <update permission="UPDATE_PATRON_STAT_CAT_ENTRY">
+                    <context link="stat_cat" field="owner"/>
+                </update>
+                <delete permission="DELETE_PATRON_STAT_CAT_ENTRY">
+                    <context link="stat_cat" field="owner"/>
+                </delete>
+                       </actions>
+               </permacrud>
        </class>
        <class id="actsced" controller="open-ils.cstore" oils_obj:fieldmapper="actor::stat_cat_entry_default" oils_persist:tablename="actor.stat_cat_entry_default" reporter:label="User Stat Cat Default Entry">
                <fields oils_persist:primary="id" oils_persist:sequence="actor.stat_cat_entry_default_id_seq">
@@ -8883,7 +8901,16 @@ SELECT  usr,
                </links>
                <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
                        <actions>
+                <create permission="CREATE_COPY_STAT_CAT_ENTRY">
+                    <context link="stat_cat" field="owner"/>
+                </create>
                                <retrieve permission="STAFF_LOGIN" global_required="true"/>
+                <update permission="UPDATE_COPY_STAT_CAT_ENTRY">
+                    <context link="stat_cat" field="owner"/>
+                </update>
+                <delete permission="DELETE_COPY_STAT_CAT_ENTRY">
+                    <context link="stat_cat" field="owner"/>
+                </delete>
                        </actions>
                </permacrud>
        </class>
index 764c473..ac5bcd9 100644 (file)
@@ -30,11 +30,14 @@ class MockAdminPageComponent {
     @Input() configLinkBasePath: string;
     @Input() defaultNewRecord: IdlObject;
     @Input() disableOrgFilter: boolean;
+    @Input() hideClearFilters: boolean;
     @Input() fieldOrder: string;
     @Input() idlClass: string;
     @Input() persistKeyPfx: string;
     @Input() readonlyFields: string;
     @Input() enableUndelete: boolean;
+    @Input() recordLabel: string;
+    @Input() orgDefaultAllowed: string;
 }
 
 describe('Component: BasicAdminPage', () => {
index cd6dbde..7332f6a 100644 (file)
@@ -11,14 +11,17 @@ import {tap, switchMap} from 'rxjs/operators';
 @Component({
     template: `
       <ng-container *ngIf="idlClass">
-      <eg-title i18n-prefix prefix="{{classLabel}} Administration">
+      <eg-title i18n-prefix prefix="{{recordLabel || classLabel}} Administration">
       </eg-title>
-      <eg-staff-banner bannerText="{{classLabel}} Configuration" i18n-bannerText>
+      <eg-staff-banner bannerText="{{recordLabel || classLabel}} Configuration" i18n-bannerText>
       </eg-staff-banner>
       <eg-admin-page persistKeyPfx="{{persistKeyPfx}}" idlClass="{{idlClass}}"
         configLinkBasePath="{{configLinkBasePath}}"
         fieldOrder="{{fieldOrder}}"
         readonlyFields="{{readonlyFields}}"
+        recordLabel="{{recordLabel}}"
+        orgDefaultAllowed="{{orgDefaultAllowed}}"
+        [hideClearFilters]="hideClearFilters"
         [defaultNewRecord]="defaultNewRecordIdl"
         [enableUndelete]="enableUndelete"
         [disableOrgFilter]="disableOrgFilter"></eg-admin-page>
@@ -33,6 +36,9 @@ export class BasicAdminPageComponent implements OnInit {
     persistKeyPfx: string;
     fieldOrder = '';
     readonlyFields = '';
+    recordLabel = '';
+    orgDefaultAllowed = '';
+    hideClearFilters: boolean;
     defaultNewRecordIdl: IdlObject;
     configLinkBasePath = '/staff/admin';
 
@@ -79,6 +85,9 @@ export class BasicAdminPageComponent implements OnInit {
                     this.enableUndelete = data['enableUndelete'];
                     this.fieldOrder = data['fieldOrder'];
                     this.readonlyFields = data['readonlyFields'];
+                    this.recordLabel = data['recordLabel'];
+                    this.orgDefaultAllowed = data['orgDefaultAllowed'];
+                    this.hideClearFilters = data['hideClearFilters'];
                     this.defaultNewRecord = data['defaultNewRecord'];
                 }
 
index 92d22f0..b5eb873 100644 (file)
       routerLink="/staff/admin/local/config/standing_penalty"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Statistical Categories Editor" 
       url="/eg/staff/admin/local/asset/stat_cat_editor"></eg-link-table-link>
+    <eg-link-table-link i18n-label label="Item Statistical Categories Editor" 
+      routerLink="/staff/admin/local/asset/stat_cat"></eg-link-table-link>
+    <eg-link-table-link i18n-label label="Patron Statistical Categories Editor" 
+      routerLink="/staff/admin/local/actor/stat_cat"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Statistical Popularity Badges" 
       routerLink="/staff/admin/local/rating/badge"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Surveys" 
index 95139c7..e0fc95d 100644 (file)
@@ -100,6 +100,48 @@ const routes: Routes = [{
     loadChildren: () =>
       import('./negative-balances/negative-balances.module').then(m => m.NegativeBalancesModule)
 }, {
+    path: 'asset/stat_cat',
+    component: BasicAdminPageComponent,
+    data: [{
+        schema: 'asset',
+        table: 'stat_cat',
+        readonlyFields: 'id',
+        orgDefaultAllowed: 'owner',
+        recordLabel: $localize `Item Statistical Category`,
+        fieldOrder: 'name,owner,required,opac_visible,checkout_archive,sip_field,sip_format'}]
+}, {
+    path: 'asset/stat_cat_entry',
+    component: BasicAdminPageComponent,
+    data: [{
+        schema: 'asset',
+        table: 'stat_cat_entry',
+        readonlyFields: 'id,stat_cat',
+        orgDefaultAllowed: 'owner',
+        recordLabel: $localize `Item Statistical Category Entry`,
+        hideClearFilters: true,
+        fieldOrder: 'stat_cat,value,owner'}]
+}, {
+    path: 'actor/stat_cat',
+    component: BasicAdminPageComponent,
+    data: [{
+        schema: 'actor',
+        table: 'stat_cat',
+        readonlyFields: 'id',
+        orgDefaultAllowed: 'owner',
+        recordLabel: $localize `Patron Statistical Category`,
+        fieldOrder: 'name,owner,required,opac_visible,usr_summary,allow_freetext,checkout_archive,sip_field,sip_format'}]
+}, {
+    path: 'actor/stat_cat_entry',
+    component: BasicAdminPageComponent,
+    data: [{
+        schema: 'actor',
+        table: 'stat_cat_entry',
+        readonlyFields: 'id,stat_cat',
+        orgDefaultAllowed: 'owner',
+        recordLabel: $localize `Patron Statistical Category Entry`,
+        hideClearFilters: true,
+        fieldOrder: 'stat_cat,value,owner'}]
+}, {
     path: ':schema/:table',
     component: BasicAdminPageComponent
 }
index 7f672d2..877dc94 100644 (file)
@@ -1,13 +1,13 @@
-<ng-template #successStrTmpl i18n>{{idlClassDef.label}} Update Succeeded</ng-template>
+<ng-template #successStrTmpl i18n>{{recordLabel || idlClassDef.label}} Update Succeeded</ng-template>
 <eg-string #successString [template]="successStrTmpl"></eg-string>
 
-<ng-template #updateFailedStrTmpl i18n>Update of {{idlClassDef.label}} failed</ng-template>
+<ng-template #updateFailedStrTmpl i18n>Update of {{recordLabel || idlClassDef.label}} failed</ng-template>
 <eg-string #updateFailedString [template]="updateFailedStrTmpl"></eg-string>
 
-<ng-template #deleteFailedStrTmpl i18n>Delete of {{idlClassDef.label}} failed or was not allowed</ng-template>
+<ng-template #deleteFailedStrTmpl i18n>Delete of {{recordLabel || idlClassDef.label}} failed or was not allowed</ng-template>
 <eg-string #deleteFailedString [template]="deleteFailedStrTmpl"></eg-string>
 
-<ng-template #deleteSuccessStrTmpl i18n>{{idlClassDef.label}} Successfully Deleted</ng-template>
+<ng-template #deleteSuccessStrTmpl i18n>{{recordLabel || idlClassDef.label}} Successfully Deleted</ng-template>
 <eg-string #deleteSuccessString [template]="deleteSuccessStrTmpl"></eg-string>
 
 <ng-template #undeleteFailedStrTmpl i18n>Undelete of {{idlClassDef.label}} failed or was not allowed</ng-template>
 <ng-template #undeleteSuccessStrTmpl i18n>{{idlClassDef.label}} Successfully undeleted</ng-template>
 <eg-string #undeleteSuccessString [template]="undeleteSuccessStrTmpl"></eg-string>
 
-<ng-template #createStrTmpl i18n>{{idlClassDef.label}} Successfully Created</ng-template>
+<ng-template #createStrTmpl i18n>{{recordLabel || idlClassDef.label}} Successfully Created</ng-template>
 <eg-string #createString [template]="createStrTmpl"></eg-string>
 
-<ng-template #createErrStrTmpl i18n>Failed to create new {{idlClassDef.label}}</ng-template>
+<ng-template #createErrStrTmpl i18n>Failed to create new {{recordLabel || idlClassDef.label}}</ng-template>
 <eg-string #createErrString [template]="createErrStrTmpl"></eg-string>
 
 <ng-container *ngIf="orgField || gridFilters">
@@ -38,7 +38,7 @@
       <div class="flex-1"></div><!-- push right -->
       <ng-container *ngIf="gridFilters">
         <span i18n>Filters Applied: {{gridFilters | json}}</span>
-        <a class="ps-2 fst-italic" 
+        <a *ngIf="!hideClearFilters" class="ps-2 fst-italic"
           [attr.href]="clearGridFiltersUrl()" i18n>Clear Filters</a>
       </ng-container>
     </div>
@@ -63,7 +63,7 @@
     (onRowActivate)="showEditDialog($event)"
     [filterable]="true" [stickyHeader]="true">
   <eg-grid-toolbar-button [disabled]="!canCreate" 
-    label="New {{idlClassDef.label}}" i18n-label (onClick)="createNew()">
+    label="New {{recordLabel || idlClassDef.label}}" i18n-label (onClick)="createNew()">
   </eg-grid-toolbar-button>
   <eg-grid-toolbar-button [disabled]="translatableFields.length === 0"
     label="Apply Translations" i18n-label (onClick)="translate()">
 </eg-grid>
 
 <eg-fm-record-editor #editDialog idlClass="{{idlClass}}" 
+    [recordLabel]="recordLabel"
     [fieldOptions]="fieldOptions"
     [fieldOrder]="fieldOrder"
     [defaultNewRecord]="defaultNewRecord"
     [preloadLinkedValues]="true"
+    [orgDefaultAllowed]="orgDefaultAllowed"
     [readonlyFields]="readonlyFields">
 </eg-fm-record-editor>
 
index d9de7bd..cf24c43 100644 (file)
@@ -82,6 +82,15 @@ export class AdminPageComponent implements OnInit {
     // Optional comma-separated list of read-only fields
     @Input() readonlyFields: string;
 
+    // Optional record label to use instead of the IDL label
+    @Input() recordLabel: string;
+
+    // optional flag to hide the Clear Filters action for gridFilters
+    @Input() hideClearFilters: boolean;
+
+    // optional list of org fields which are allowed a default if unset
+    @Input() orgDefaultAllowed: string;
+
     // Optional template containing help/about text which will
     // be added to the page, above the grid.
     @Input() helpTemplate: TemplateRef<any>;
index 70b5440..c7a9dcd 100644 (file)
@@ -29,3 +29,5 @@
     UPDATE_COPY permission the new UPDATE_COPY_BARCODE permission at the
     same depth, though it's technically not needed.
 * Patron and staff login forms now include a button to reveal the password input. (LP#1977554)
+* Adds new Local Administration entries for Item Statistical Categories Editor and Patron Statistical Categories Editor, which are angularized interfaces.
+* Tweaks eg-grids to underline hyperlinks within cells.  This potentially affects multiple interfaces.