LP1840050 org unit admin UI WIP
authorBill Erickson <berickxx@gmail.com>
Mon, 12 Aug 2019 20:16:01 +0000 (16:16 -0400)
committerBill Erickson <berickxx@gmail.com>
Tue, 13 Aug 2019 14:46:54 +0000 (10:46 -0400)
Angular port of Admin => Server Administration => Organizational Units.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/eg2/src/app/share/org-select/org-select.component.html
Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html
Open-ILS/src/eg2/src/app/staff/admin/server/admin-server.module.ts
Open-ILS/src/eg2/src/app/staff/admin/server/org-unit.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/server/org-unit.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts

index d28d708..7b9bf77 100644 (file)
@@ -6742,14 +6742,14 @@ SELECT  usr,
                        <field reporter:label="Organizational Unit ID" name="id" reporter:datatype="org_unit" reporter:selector="shortname"/>
                        <field reporter:label="ILL Receiving Address" name="ill_address" reporter:datatype="link"/>
                        <field reporter:label="Mailing Address" name="mailing_address" reporter:datatype="link"/>
-                       <field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true"/>
-                       <field reporter:label="Organizational Unit Type" name="ou_type" reporter:datatype="link"/>
+                       <field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true" oils_obj:required="true"/>
+                       <field reporter:label="Organizational Unit Type" name="ou_type" reporter:datatype="link" oils_obj:required="true"/>
                        <field reporter:label="Parent Organizational Unit" name="parent_ou" reporter:datatype="link"/>
                        <field reporter:label="Short (Policy) Name" name="shortname" reporter:datatype="text" oils_obj:required="true" oils_obj:validate="^.+$"/>
                        <field reporter:label="Email Address" name="email" reporter:datatype="text"/>
                        <field reporter:label="Phone Number" name="phone" reporter:datatype="text"/>
                        <field reporter:label="OPAC Visible" name="opac_visible" reporter:datatype="bool"/>
-                       <field reporter:label="Fiscal Calendar" name="fiscal_calendar" reporter:datatype="link"/>
+                       <field reporter:label="Fiscal Calendar" name="fiscal_calendar" reporter:datatype="link" oils_obj:required="true"/>
                        <field reporter:label="Users" name="users" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Closed Dates" name="closed_dates" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Circulations" name="circulations" oils_persist:virtual="true" reporter:datatype="link"/>
index d49217c..32bc94e 100644 (file)
@@ -4,7 +4,7 @@
 {{r.label}}
 </ng-template>
 
-<ng-container *ngIf="readOnly">
+<ng-container *ngIf="readOnly && selected">
   <span>{{selected.label}}</span>
 </ng-container>
 
index 99cb478..7c62f20 100644 (file)
@@ -76,7 +76,7 @@
     <eg-link-table-link i18n-label label="Org Unit Setting Types"  
       routerLink="/staff/admin/server/config/org_unit_setting_type"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Organizational Units"  
-      url="/eg/staff/admin/server/legacy/actor/org_unit"></eg-link-table-link>
+      routerLink="/staff/admin/server/actor/org_unit"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Permission Groups"  
       url="/eg/staff/admin/server/legacy/permission/grp_tree"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Permissions"  
index 8e76239..a7fe825 100644 (file)
@@ -5,11 +5,13 @@ import {AdminServerRoutingModule} from './routing.module';
 import {AdminCommonModule} from '@eg/staff/admin/common.module';
 import {AdminServerSplashComponent} from './admin-server-splash.component';
 import {OrgUnitTypeComponent} from './org-unit-type.component';
+import {OrgUnitComponent} from './org-unit.component';
 
 @NgModule({
   declarations: [
       AdminServerSplashComponent,
-      OrgUnitTypeComponent
+      OrgUnitTypeComponent,
+      OrgUnitComponent
   ],
   imports: [
     AdminCommonModule,
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/org-unit.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/org-unit.component.html
new file mode 100644 (file)
index 0000000..a1ef44a
--- /dev/null
@@ -0,0 +1,38 @@
+<eg-staff-banner bannerText="Org Unit Configuration" i18n-bannerText>
+</eg-staff-banner>
+
+<ng-template #editStrTmpl i18n>Org Unit Update Succeeded</ng-template>
+<eg-string #editString [template]="editStrTmpl"></eg-string>
+
+<ng-template #createStrTmpl i18n>Org Unit Succeessfully Created</ng-template>
+<eg-string #createString [template]="createStrTmpl"></eg-string>
+
+<ng-template #errorStrTmpl i18n>Org Unit Update Failed</ng-template>
+<eg-string #errorString [template]="errorStrTmpl"></eg-string>
+
+<eg-confirm-dialog #delConfirm 
+  i18n-dialogTitle i18n-dialogBody
+  dialogTitle="Confirm Delete"
+  dialogBody="Delete Org Unit {{selected.label}}?">
+</eg-confirm-dialog>
+
+<div class="row">
+  <div class="col-lg-4">
+    <h3 i18n>Org Units</h3>
+    <eg-tree [tree]="tree" (nodeClicked)="nodeClicked($event)"></eg-tree>
+  </div>
+  <div class="col-lg-8">
+    <ngb-tabset #rootTabs [activeId]="main">
+      <ngb-tab title="Main Settings" i18n-title id="main">
+        <ng-template ngbTabContent>
+          <eg-fm-record-editor #editDialog idlClass="aou" [displayMode]="inline" 
+            [mode]="selected ? 'update' : 'create'"
+            readonlyFields="parent,ou_type,parent_ou" [recordId]="selected ? selected.id : null" 
+            fieldOrder="parent_ou,ou_type,name,shortname,phone,email,opac_visible,fiscal_calendar"
+            hiddenFields="id,billing_address,mailing_address,holds_address,ill_address">
+          </eg-fm-record-editor>
+        </ng-template>
+      </ngb-tab>
+    </ngb-tabset>
+  </div>
+</div>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/org-unit.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/org-unit.component.ts
new file mode 100644 (file)
index 0000000..65b21d0
--- /dev/null
@@ -0,0 +1,143 @@
+import {Component, Input, ViewChild, OnInit} from '@angular/core';
+import {Tree, TreeNode} from '@eg/share/tree/tree';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.service';
+import {AuthService} from '@eg/core/auth.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+
+@Component({
+    templateUrl: './org-unit.component.html'
+})
+
+export class OrgUnitComponent implements OnInit {
+
+    tree: Tree;
+    selected: TreeNode;
+    @ViewChild('editDialog') editDialog: FmRecordEditorComponent;
+    @ViewChild('createString') createString: StringComponent;
+    @ViewChild('editString') editString: StringComponent;
+    @ViewChild('errorString') errorString: StringComponent;
+    @ViewChild('delConfirm') delConfirm: ConfirmDialogComponent;
+
+    constructor(
+        private idl: IdlService,
+        private org: OrgService,
+        private auth: AuthService,
+        private pcrud: PcrudService,
+        private toast: ToastService
+    ) {}
+
+
+    ngOnInit() {
+        // On initial page load, use the existing org tree data and
+        // select the root node.
+        this.ingestAouTree(this.org.tree());
+        this.selected = this.tree.rootNode;
+    }
+
+    loadAouTree() {
+        this.org.fetchOrgs().then(() => this.ingestAouTree(this.org.tree()));
+    }
+
+    // Translate the org unt type tree into a structure EgTree can use.
+    ingestAouTree(aouTree) {
+
+        const handleNode = (orgNode: IdlObject): TreeNode => {
+            if (!orgNode) { return; }
+
+            // Clone to avoid modifying the shared org tree
+            const node = this.idl.clone(orgNode);
+            node.ou_type(node.ou_type().id());
+
+            const treeNode = new TreeNode({
+                id: node.id(),
+                label: node.name(),
+                callerData: {orgUnit: node}
+            });
+
+            node.children().forEach(childNode =>
+                treeNode.children.push(handleNode(childNode))
+            );
+
+            return treeNode;
+        };
+
+        const rootNode = handleNode(aouTree);
+        this.tree = new Tree(rootNode);
+    }
+
+    nodeClicked($event: any) {
+        this.selected = $event;
+    }
+
+    postUpdate(message: StringComponent) {
+        // Modifying org unit types means refetching the org unit
+        // data normally fetched on page load, since it includes
+        // org unit type data.
+        this.org.fetchOrgs().then(() =>
+            message.current().then(str => this.toast.success(str)));
+    }
+
+    remove() {
+        this.delConfirm.open().subscribe(confirmed => {
+            if (!confirmed) { return; }
+
+            const org = this.selected.callerData.orgUnit;
+
+            this.pcrud.remove(org).subscribe(
+                ok2 => {},
+                err => {
+                    this.errorString.current()
+                      .then(str => this.toast.danger(str));
+                },
+                ()  => {
+                    // Avoid updating until we know the entire
+                    // pcrud action/transaction completed.
+                    this.loadAouTree();
+
+                    // After removal, select the parent org if available
+                    // otherwise the root org.
+                    const orgId = org.parent_ou() ? org.parent_ou() : this.org.root().id();
+                    this.selected = this.tree.findNode(orgId);
+                    this.postUpdate(this.editString);
+                }
+            );
+        });
+    }
+
+    addChild() {
+        const parentTreeNode = this.selected;
+        const parentOrg = parentTreeNode.callerData.orgUnit;
+
+        const newOrg = this.idl.create('aou');
+        newOrg.parent(parentOrg.id());
+
+        // TODO UNSUBSCRIBE
+        this.editDialog.onSave$.subscribe(result => { // aou object
+
+            // Add our new node to the tree
+            const newNode = new TreeNode({
+                id: result.id(),
+                label: result.name(),
+                callerData: {orgUnit: result}
+            });
+            this.loadAouTree();
+            this.postUpdate(this.createString);
+        });
+
+        // TODO subscribe / unsub error events
+
+            /*
+            rejected => {
+                this.errorString.current()
+                    .then(str => this.toast.danger(str));
+            }
+        );
+        */
+    }
+}
+
index c971ed7..aa4ec31 100644 (file)
@@ -3,6 +3,7 @@ import {RouterModule, Routes} from '@angular/router';
 import {AdminServerSplashComponent} from './admin-server-splash.component';
 import {BasicAdminPageComponent} from '@eg/staff/admin/basic-admin-page.component';
 import {OrgUnitTypeComponent} from './org-unit-type.component';
+import {OrgUnitComponent} from './org-unit.component';
 
 const routes: Routes = [{
     path: 'splash',
@@ -11,6 +12,9 @@ const routes: Routes = [{
     path: 'actor/org_unit_type',
     component: OrgUnitTypeComponent
 }, {
+    path: 'actor/org_unit',
+    component: OrgUnitComponent
+}, {
     path: ':schema/:table',
     component: BasicAdminPageComponent
 }];