lp1840287 Edit Floating Group Members
authorMike Risher <mrisher@catalyte.io>
Mon, 9 Sep 2019 17:47:48 +0000 (17:47 +0000)
committerMike Risher <mrisher@catalyte.io>
Mon, 9 Sep 2019 19:16:50 +0000 (19:16 +0000)
Add functionality so that when editing floating group members, one
is taken to a page where one can edit a given floating group as
well as all its linked members.

Signed-off-by: Mike Risher <mrisher@catalyte.io>
 Changes to be committed:
modified:   Open-ILS/src/eg2/src/app/staff/admin/server/admin-server.module.ts
new file:   Open-ILS/src/eg2/src/app/staff/admin/server/edit-floating-group.component.html
new file:   Open-ILS/src/eg2/src/app/staff/admin/server/edit-floating-group.component.ts
new file:   Open-ILS/src/eg2/src/app/staff/admin/server/floating-group.component.html
new file:   Open-ILS/src/eg2/src/app/staff/admin/server/floating-group.component.ts
modified:   Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts

Open-ILS/src/eg2/src/app/staff/admin/server/admin-server.module.ts
Open-ILS/src/eg2/src/app/staff/admin/server/edit-floating-group.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/server/edit-floating-group.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/server/floating-group.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/server/floating-group.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts

index cbbadd3..0d78a26 100644 (file)
@@ -4,6 +4,8 @@ import {StaffCommonModule} from '@eg/staff/common.module';
 import {AdminServerRoutingModule} from './routing.module';
 import {AdminCommonModule} from '@eg/staff/admin/common.module';
 import {AdminServerSplashComponent} from './admin-server-splash.component';
+import {FloatingGroupComponent} from './floating-group.component';
+import {EditFloatingGroupComponent} from './edit-floating-group.component';
 import {OrgUnitTypeComponent} from './org-unit-type.component';
 import {PrintTemplateComponent} from './print-template.component';
 import {SampleDataService} from '@eg/share/util/sample-data.service';
@@ -12,7 +14,9 @@ import {SampleDataService} from '@eg/share/util/sample-data.service';
   declarations: [
       AdminServerSplashComponent,
       OrgUnitTypeComponent,
-      PrintTemplateComponent
+      PrintTemplateComponent,
+      FloatingGroupComponent,
+      EditFloatingGroupComponent
   ],
   imports: [
     AdminCommonModule,
@@ -30,3 +34,6 @@ export class AdminServerModule {
 }
 
 
+
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/edit-floating-group.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/edit-floating-group.component.html
new file mode 100644 (file)
index 0000000..6cd87cd
--- /dev/null
@@ -0,0 +1,10 @@
+<eg-title i18n-prefix prefix="Edit Floating Group"></eg-title>
+<eg-staff-banner bannerText="Edit Floating Group" i18n-bannerText>
+</eg-staff-banner>
+<eg-fm-record-editor displayMode="inline"
+    idlClass="cfg" mode="update" recordId="{{this.currentId}}">
+</eg-fm-record-editor>
+<eg-staff-banner bannerText="Edit Floating Group Members" i18n-bannerText>
+    </eg-staff-banner>
+<eg-admin-page idlClass="cfgm" disableOrgFilter="true" 
+    [dataSource]="dataSource"></eg-admin-page>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/edit-floating-group.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/edit-floating-group.component.ts
new file mode 100644 (file)
index 0000000..bd7370e
--- /dev/null
@@ -0,0 +1,41 @@
+import {Component, Input} from '@angular/core';
+import {Router} from '@angular/router';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {Pager} from '@eg/share/util/pager';
+import {PcrudService} from '@eg/core/pcrud.service';
+
+ @Component({
+     templateUrl: './edit-floating-group.component.html'
+ })
+
+ export class EditFloatingGroupComponent {
+
+    @Input() idlClass = 'cfgm';
+    @Input() sortField: string;
+    @Input() dataSource: GridDataSource;
+    @Input() dialogSize: 'sm' | 'lg' = 'lg';
+    
+    // This is the ID of the floating group being edited currently
+    currentId: string;
+
+    constructor(
+        private pcrud: PcrudService,
+        private router: Router
+    ) {
+    }
+
+    ngOnInit() {
+        let url = this.router.routerState.snapshot.url;
+        let slash = url.lastIndexOf('/');
+        this.currentId = url.substring(slash+1);
+        this.dataSource = new GridDataSource();
+        this.dataSource.getRows = (pager: Pager, sort: any[]) => {
+            const searchOps = {
+                offset: pager.offset,
+                limit: pager.limit,
+                order_by: {}
+            };            
+            return this.pcrud.search(this.idlClass, {floating_group: this.currentId}, searchOps);
+        };
+    }
+ }
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/floating-group.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/floating-group.component.html
new file mode 100644 (file)
index 0000000..6f26411
--- /dev/null
@@ -0,0 +1,68 @@
+<eg-title i18n-prefix prefix="Floating Group Administration"></eg-title>
+<eg-staff-banner bannerText="Floating Group Configuration4" i18n-bannerText>
+</eg-staff-banner>
+<!-- <eg-admin-page persistKeyPfx="server" idlClass="cfg"></eg-admin-page> -->
+<!-- <eg-staff-banner bannerText="Linked Group Members" i18n-bannerText>
+</eg-staff-banner>
+<eg-admin-page persistKeyPfx="server" idlClass="cfgm"></eg-admin-page> -->
+
+
+<!-- <ng-template #successStrTmpl i18n>{{idlClassDef.label}} Update Succeeded</ng-template>
+<eg-string #successString [template]="successStrTmpl"></eg-string>
+
+<ng-template #createStrTmpl i18n>{{idlClassDef.label}} Succeessfully Created</ng-template>
+<eg-string #createString [template]="createStrTmpl"></eg-string> -->
+
+<!-- <ng-container *ngIf="orgField">
+  <div class="d-flex">
+    <div>
+      <div class="input-group">
+        <div class="input-group-prepend">
+          <span class="input-group-text">{{orgFieldLabel}}</span>
+        </div>
+        <eg-org-select 
+          [limitPerms]="viewPerms"
+          [initialOrg]="contextOrg"
+          (onChange)="orgOnChange($event)">
+        </eg-org-select>
+      </div>
+    </div>
+    <div class="pl-2">
+      <div class="form-check">
+        <input type="checkbox" (click)="grid.reload()" 
+          [disabled]="disableAncestorSelector()"
+          [(ngModel)]="includeOrgAncestors"
+          class="form-check-input" id="include-ancestors">
+        <label class="form-check-label" for="include-ancestors" i18n>+ Ancestors</label>
+      </div>
+      <div class="form-check">
+        <input type="checkbox" (click)="grid.reload()" 
+          [disabled]="disableDescendantSelector()"
+          [(ngModel)]="includeOrgDescendants" 
+          class="form-check-input" id="include-descendants">
+        <label class="form-check-label" for="include-descendants" i18n>+ Descendants</label>
+      </div>
+    </div>
+  </div>
+  <hr/>
+</ng-container> -->
+
+<!-- idlObject and fieldName applied programmatically -->
+<!-- <eg-translate #translator></eg-translate> -->
+
+<eg-grid #grid idlClass="{{idlClass}}" [dataSource]="dataSource" 
+    [sortable]="true" persistKey="{{persistKey}}">
+  <eg-grid-toolbar-button [disabled]="!canCreate" 
+    label="New {{idlClassDef.label}}" i18n-label [action]="createNew">
+  </eg-grid-toolbar-button>
+  <!-- <eg-grid-toolbar-button [disabled]="translatableFields.length == 0" 
+    label="Apply Translations" i18n-label [action]="translate">
+  </eg-grid-toolbar-button> -->
+  <eg-grid-toolbar-action label="Delete Selected" i18n-label [action]="deleteSelected">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action label="Edit Selected" i18n-label [action]="editSelected">
+  </eg-grid-toolbar-action>
+</eg-grid>
+
+<!-- <eg-fm-record-editor #editDialog idlClass="{{idlClass}}">
+</eg-fm-record-editor> -->
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/floating-group.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/floating-group.component.ts
new file mode 100644 (file)
index 0000000..ea02f22
--- /dev/null
@@ -0,0 +1,291 @@
+// import {Component} from '@angular/core';
+import {Component, Input, OnInit, TemplateRef, ViewChild} from '@angular/core';
+import { RouterModule, Routes,   CanActivate, Router,
+    ActivatedRouteSnapshot,
+    RouterStateSnapshot,
+    CanActivateChild }    from '@angular/router';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {TranslateComponent} from '@eg/staff/share/translate/translate.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {Pager} from '@eg/share/util/pager';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {OrgService} from '@eg/core/org.service';
+import {PermService} from '@eg/core/perm.service';
+import {AuthService} from '@eg/core/auth.service';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+import {StringComponent} from '@eg/share/string/string.component';
+
+ @Component({
+     templateUrl: './floating-group.component.html'
+ })
+
+ export class FloatingGroupComponent {
+
+    @Input() idlClass: string = "cfg";
+
+    // Default sort field, used when no grid sorting is applied.
+    @Input() sortField: string;
+
+    // Data source may be provided by the caller.  This gives the caller
+    // complete control over the contents of the grid.  If no data source
+    // is provided, a generic one is create which is sufficient for data
+    // that requires no special handling, filtering, etc.
+    @Input() dataSource: GridDataSource;
+
+    // Size of create/edito dialog.  Uses large by default.
+    @Input() dialogSize: 'sm' | 'lg' = 'lg';
+
+    // If an org unit field is specified, an org unit filter
+    // is added to the top of the page.
+    @Input() orgField: string;
+
+    // Disable the auto-matic org unit field filter
+    @Input() disableOrgFilter: boolean;
+
+    // Include objects linking to org units which are ancestors
+    // of the selected org unit.
+    @Input() includeOrgAncestors: boolean;
+
+    // Ditto includeOrgAncestors, but descendants.
+    @Input() includeOrgDescendants: boolean;
+
+    // Optional grid persist key.  This is the part of the key
+    // following eg.grid.
+    @Input() persistKey: string;
+
+    // Optional path component to add to the generated grid persist key,
+    // formatted as (for example):
+    // 'eg.grid.admin.${persistKeyPfx}.config.billing_type'
+    @Input() persistKeyPfx: string = "server";
+
+    @ViewChild('grid') grid: GridComponent;
+    @ViewChild('editDialog') editDialog: FmRecordEditorComponent;
+    @ViewChild('successString') successString: StringComponent;
+    @ViewChild('createString') createString: StringComponent;
+    @ViewChild('translator') translator: TranslateComponent;
+
+    idlClassDef: any;
+    pkeyField: string;
+    createNew: () => void;
+    deleteSelected: (rows: IdlObject[]) => void;
+    editSelected: (rows: IdlObject[]) => void;
+
+    // True if any columns on the object support translations
+    translateRowIdx: number;
+    translateFieldIdx: number;
+    translatableFields: string[];
+    translate: () => void;
+
+    contextOrg: IdlObject;
+    orgFieldLabel: string;
+    viewPerms: string;
+    canCreate: boolean;
+
+    constructor(
+        private idl: IdlService,
+        private org: OrgService,
+        private auth: AuthService,
+        private pcrud: PcrudService,
+        private perm: PermService,
+        private toast: ToastService,
+        private router: Router
+    ) {
+        this.translatableFields = [];
+    }
+
+    applyOrgValues() {
+
+        if (this.disableOrgFilter) {
+            this.orgField = null;
+            return;
+        }
+
+        if (!this.orgField) {
+            // If no org unit field is specified, try to find one.
+            // If an object type has multiple org unit fields, the
+            // caller should specify one or disable org unit filter.
+            this.idlClassDef.fields.forEach(field => {
+                if (field['class'] === 'aou') {
+                    this.orgField = field.name;
+                }
+            });
+        }
+
+        if (this.orgField) {
+            this.orgFieldLabel = this.idlClassDef.field_map[this.orgField].label;
+            this.contextOrg = this.org.root();
+        }
+    }
+
+    ngOnInit() {
+        console.log("ngOnInit running in fg component")
+        this.idlClassDef = this.idl.classes[this.idlClass];
+        this.pkeyField = this.idlClassDef.pkey || 'id';
+
+        // this.translatableFields =
+        //     this.idlClassDef.fields.filter(f => f.i18n).map(f => f.name);
+
+        if (!this.persistKey) {
+            this.persistKey =
+                'admin.' +
+                (this.persistKeyPfx ? this.persistKeyPfx + '.' : '') +
+                this.idlClassDef.table;
+        }
+
+        // Limit the view org selector to orgs where the user has
+        // permacrud-encoded view permissions.
+        const pc = this.idlClassDef.permacrud;
+        if (pc && pc.retrieve) {
+            this.viewPerms = pc.retrieve.perms;
+        }
+
+        this.checkCreatePerms();
+        this.applyOrgValues();
+
+        // If the caller provides not data source, create a generic one.
+        if (!this.dataSource) {
+            this.initDataSource();
+        }
+
+        // TODO: pass the row activate handler via the grid markup
+        // this.grid.onRowActivate.subscribe(
+        //     (idlThing: IdlObject) => {
+        //         console.log("grid.onrowactivate fg component")
+        //         this.editDialog.mode = 'update';
+        //         this.editDialog.recId = idlThing[this.pkeyField]();
+        //         this.editDialog.open({size: this.dialogSize}).then(
+        //             ok => {
+        //                 this.successString.current()
+        //                     .then(str => this.toast.success(str));
+        //                 this.grid.reload();
+        //             },
+        //             err => {}
+        //         );
+        //     }
+        // );
+
+        // this.createNew = () => {
+        //     this.editDialog.mode = 'create';
+        //     this.editDialog.open({size: this.dialogSize}).then(
+        //         ok => {
+        //             this.createString.current()
+        //                 .then(str => this.toast.success(str));
+        //             this.grid.reload();
+        //         },
+        //         err => {}
+        //     );
+        // };
+
+        this.deleteSelected = (floatingGroups: IdlObject[]) => {
+            floatingGroups.forEach(fg => fg.isdeleted(true));
+            this.pcrud.autoApply(floatingGroups).subscribe(
+                val => console.debug('deleted: ' + val),
+                err => {},
+                ()  => this.grid.reload()
+            );
+        };
+
+        this.editSelected = (floatingGroups: IdlObject[]) => {
+
+            // console.log(floatingGroups);
+            // console.log("route to this id:")
+            // console.log(floatingGroups[0].a[0]);
+            // console.log("route to this id:")
+            let idToEdit = floatingGroups[0].a[0];
+            console.log("you clicked edit. We are editing id=" + idToEdit);
+            this.router.navigate(["/staff/admin/server/config/floating_group/" + idToEdit]);
+            // Edit each IDL thing one at a time
+            // const editOneThing = (standingPenalty: IdlObject) => {
+            //     if (!standingPenalty) { return; }
+
+            //     this.showEditDialog(standingPenalty).then(
+            //         () => editOneThing(standingPenaltyFields.shift()));
+            // };
+
+            // editOneThing(standingPenaltyFields.shift());
+        }
+    }
+
+    checkCreatePerms() {
+        this.canCreate = false;
+        const pc = this.idlClassDef.permacrud || {};
+        const perms = pc.create ? pc.create.perms : [];
+        if (perms.length === 0) { return; }
+
+        this.perm.hasWorkPermAt(perms, true).then(permMap => {
+            Object.keys(permMap).forEach(key => {
+                if (permMap[key].length > 0) {
+                    this.canCreate = true;
+                }
+            });
+        });
+    }
+
+    orgOnChange(org: IdlObject) {
+        this.contextOrg = org;
+        this.grid.reload();
+    }
+
+    initDataSource() {
+        this.dataSource = new GridDataSource();
+
+        this.dataSource.getRows = (pager: Pager, sort: any[]) => {
+            const orderBy: any = {};
+
+            if (sort.length) {
+                // Sort specified from grid
+                orderBy[this.idlClass] = sort[0].name + ' ' + sort[0].dir;
+
+            } else if (this.sortField) {
+                // Default sort field
+                orderBy[this.idlClass] = this.sortField;
+            }
+
+            const searchOps = {
+                offset: pager.offset,
+                limit: pager.limit,
+                order_by: orderBy
+            };
+
+            if (this.contextOrg) {
+                // Filter rows by those linking to the context org and
+                // optionally ancestor and descendant org units.
+
+                let orgs = [this.contextOrg.id()];
+
+                if (this.includeOrgAncestors) {
+                    orgs = this.org.ancestors(this.contextOrg, true);
+                }
+
+                if (this.includeOrgDescendants) {
+                    // can result in duplicate workstation org IDs... meh
+                    orgs = orgs.concat(
+                        this.org.descendants(this.contextOrg, true));
+                }
+
+                const search = {};
+                search[this.orgField] = orgs;
+                return this.pcrud.search(this.idlClass, search, searchOps);
+            }
+
+            // No org filter -- fetch all rows
+            return this.pcrud.retrieveAll(this.idlClass, searchOps);
+        };
+    }
+
+    disableAncestorSelector(): boolean {
+        return this.contextOrg &&
+            this.contextOrg.id() === this.org.root().id();
+    }
+
+    disableDescendantSelector(): boolean {
+        return this.contextOrg && this.contextOrg.children().length === 0;
+    }
+
+
+
+
+
+ }
\ No newline at end of file
index 4f9b9ff..1f4bbe5 100644 (file)
@@ -1,11 +1,19 @@
 import {NgModule} from '@angular/core';
 import {RouterModule, Routes} from '@angular/router';
 import {AdminServerSplashComponent} from './admin-server-splash.component';
+import {FloatingGroupComponent} from './floating-group.component';
+import {EditFloatingGroupComponent} from './edit-floating-group.component';
 import {BasicAdminPageComponent} from '@eg/staff/admin/basic-admin-page.component';
 import {OrgUnitTypeComponent} from './org-unit-type.component';
 import {PrintTemplateComponent} from './print-template.component';
 
 const routes: Routes = [{
+    path: 'config/floating_group',
+    component: FloatingGroupComponent
+}, {
+    path: 'config/floating_group/:id',
+    component: EditFloatingGroupComponent
+}, {
     path: 'splash',
     component: AdminServerSplashComponent
 }, {