lp1849212 Move Course Reserves to Local Admin
authorKyle Huckins <khuckins@catalyte.io>
Mon, 25 Nov 2019 18:46:59 +0000 (18:46 +0000)
committerGalen Charlton <gmc@equinoxinitiative.org>
Mon, 14 Sep 2020 22:16:26 +0000 (18:16 -0400)
- Move the Course Reserves admin UI to Local Admin
instead of Server Admin

Signed-off-by: Kyle Huckins <khuckins@catalyte.io>
Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu>
modified:   Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
renamed:    Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-associate-material.component.html -> Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-associate-material.component.html
renamed:    Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-associate-material.component.ts -> Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-associate-material.component.ts
renamed:    Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-list.component.html -> Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-list.component.html
renamed:    Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-list.component.ts -> Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-list.component.ts
renamed:    Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-reserves.module.ts -> Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-reserves.module.ts
renamed:    Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course.service.ts -> Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course.service.ts
renamed:    Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/routing.module.ts -> Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/routing.module.ts
modified:   Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts
modified:   Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html
modified:   Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts

Signed-off-by: Michele Morgan <mmorgan@noblenet.org>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
18 files changed:
Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-associate-material.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-associate-material.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-list.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-list.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-reserves.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course.service.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/routing.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts
Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html
Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-associate-material.component.html [deleted file]
Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-associate-material.component.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-list.component.html [deleted file]
Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-list.component.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-reserves.module.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course.service.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/routing.module.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts

index dde4a35..b18dda9 100644 (file)
@@ -23,6 +23,8 @@
       url="/eg/staff/admin/local/config/circ_matrix_matchpoint"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Closed Dates Editor" 
       url="/eg/staff/admin/local/actor/closed_dates"></eg-link-table-link>
+    <eg-link-table-link i18n-label label="Course Reserves List"  
+      routerLink="/staff/admin/local/asset/course_list"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Event Definition Group Members"
       routerLink="/staff/admin/local/action_trigger/event_def_group_member"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Event Definition Groups"
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-associate-material.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-associate-material.component.html
new file mode 100644 (file)
index 0000000..488eea7
--- /dev/null
@@ -0,0 +1,160 @@
+<eg-string #deleteFailedString i18n-text text="Disassociation of Course Material failed or was not allowed"></eg-string>
+<eg-string #deleteSuccessString i18n-text text="Disassociation of Course Material succeeded"></eg-string>
+<eg-string #successString i18n-text text="Association of Course Material succeeded"></eg-string>
+<eg-string #failedString i18n-text text="Association of Course Material failed or was not allowed"></eg-string>
+<eg-string #differentLibraryString i18n-text text="Material exists at a different library"></eg-string>
+
+<ng-template #dialogContent>
+  <div class="modal-header bg-info">
+    <h4 class="modal-title" i18n>Course Materials</h4>
+    <button type="button" class="close"
+      i18n-aria-label aria-label="Close" (click)="close()">
+      <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body">
+    <div class="row mt-3">
+      <div class="col-md-4">
+        <div class="input-group">
+          <div class="input-group-prepend">
+            <span class="input-group-text" i18n>Barcode</span>
+          </div>
+          <input type="text" [(ngModel)]="barcodeInput" />
+        </div>
+      </div>
+      <div class="col-md-5">
+        <div class="input-group">
+          <div class="input-group-prepend">
+            <span class="input-group-text" i18n>Relationship</span>
+          </div>
+          <input type="text" [(ngModel)]="relationshipInput" placeholder-i18n placeholder="e.g. Required" />
+        </div>
+      </div>
+      <div class="col-md-3">
+        <button class="btn btn-outline-dark" (click)="associateItem(barcodeInput, relationshipInput)" i18n [disabled]="!barcodeInput">Add Material</button>
+      </div>
+    </div>
+    <div class="row justify-content-center mt-3">
+      <div class="col">
+        <h5 i18n>The following fields will be applied to the material added, and reverted once the course is no longer associated with the material.</h5>
+      </div>
+    </div>
+    <div class="row mt-3">
+      <div class="col-md-6">
+        <div class="input-group">
+          <div class="input-group-prepend">
+            <div class="input-group-text">
+              <span i18n>Call Number</span>
+            </div>
+          </div>
+          <input type="text" [(ngModel)]="tempCallNumber"
+            (input)="isModifyingCallNumber = true"/>
+          <div class="input-group-append">
+            <div class="input-group-text">
+              <input type="checkbox" [(ngModel)]="isModifyingCallNumber"
+                aria-label="Checkbox for setting a temporary Call Number" />
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="col-md-6">
+        <div class="input-group">
+          <div class="input-group-prepend">
+            <div class="input-group-text">
+              <span i18n>Circulation Modifier</span>
+            </div>
+          </div>
+          <eg-combobox i18n-placeholder placeholder="Circulation Modifier..."
+            idlClass="ccm" idlField="name" [displayTemplate]="idlClassLabel"
+            [asyncSupportsEmptyTermClick]="true"
+            (onChange)="tempCircMod = $event.id; isModifyingCircMod = true">
+          </eg-combobox>
+          <div class="input-group-append">
+            <div class="input-group-text">
+              <input type="checkbox" [(ngModel)]="isModifyingCircMod"
+                aria-label="Checkbox for setting a temporary Circulation Modifier" />
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="row mt-3">
+      <div class="col-md-6">
+        <div class="input-group">
+          <div class="input-group-prepend">
+            <div class="input-group-text">
+              <span i18n>Item Status</span>
+            </div>
+          </div>
+          <eg-combobox i18n-placeholder placeholder="Item Status..."
+            idlClass="ccs" idlField="name" [displayTemplate]="idlClassLabel"
+            [asyncSupportsEmptyTermClick]="true"
+            (onChange)="tempStatus = $event.id; isModifyingStatus = true">
+          </eg-combobox>
+          <div class="input-group-append">
+            <div class="input-group-text">
+              <input type="checkbox" [(ngModel)]="isModifyingStatus"
+                aria-label="Checkbox for setting a temporary Item Status" />
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="col-md-6">
+        <div class="input-group">
+          <div class="input-group-prepend">
+            <div class="input-group-text">
+              <span i18n>Shelving Location</span>
+            </div>
+          </div>
+          <eg-item-location-select permFilter="MANAGE_RESERVES"
+            [(ngModel)]="tempLocation" (oninput)="isModifyingLocation = true">
+          </eg-item-location-select>
+          <div class="input-group-append">
+            <div class="input-group-text">
+              <input type="checkbox" [(ngModel)]="isModifyingLocation"
+                aria-label="Checkbox for setting a temporary Shelving Location" />
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="mt-3">
+      <eg-grid #materialGrid [dataSource]="gridDataSource">
+        <eg-grid-toolbar-action label="Delete Selected" i18n-label (onClick)="deleteSelected($event)">
+        </eg-grid-toolbar-action>
+
+        <eg-grid-column path="id" [index]=true [hidden]="true" label="ID" i18n-label></eg-grid-column>
+        <eg-grid-column label="Barcode" i18n-label name="barcode" [cellTemplate]="barcodeCellTemplate"></eg-grid-column>
+        <eg-grid-column label="Title" i18n-label name="title" [cellTemplate]="titleCellTemplate"></eg-grid-column>
+        <eg-grid-column path="call_number.label" label="Call Number" i18n-label></eg-grid-column>
+        <eg-grid-column path="call_number.prefix.label" [hidden]="true" label="Call Number Prefix" i18n-label hidden></eg-grid-column>
+        <eg-grid-column path="call_number.suffix.label" [hidden]="true" label="Call Number Suffix" i18n-label hidden></eg-grid-column>
+        <eg-grid-column path="circ_modifier" [hidden]="true" label="Circulation Modifier" i18n-label></eg-grid-column>
+        <eg-grid-column path="circ_lib.shortname" label="Circulation Library" i18n-label></eg-grid-column>
+        <eg-grid-column path="location.name" [hidden]="true" label="Shelving Location" i18n-label></eg-grid-column>
+        <eg-grid-column path="status.name" [hidden]="true" label="Copy Status" i18n-label></eg-grid-column>
+        <eg-grid-column path="_relationship" label="Relationship" i18n-label></eg-grid-column>
+      </eg-grid>
+    </div>
+  </div>
+  <ng-template #barcodeCellTemplate let-entry="row">
+    <span>
+      <a class="pl-1"
+        href="/eg/staff/cat/item/{{entry.id()}}">
+        {{entry.barcode()}}
+      </a>
+    </span>
+  </ng-template>
+  <ng-template #titleCellTemplate let-entry="row">
+    <span>
+      <a class="pl-1"
+        href="/eg/staff/cat/catalog/record/{{entry.call_number().record()}}">
+        {{entry._title}}
+      </a>
+    </span>
+  </ng-template>
+  <ng-template #idlClassLabel let-r="result" i18n>
+    {{r.label}}
+  </ng-template>
+</ng-template>
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-associate-material.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-associate-material.component.ts
new file mode 100644 (file)
index 0000000..cb8eeeb
--- /dev/null
@@ -0,0 +1,174 @@
+import {Component, Input, ViewChild, OnInit, TemplateRef} from '@angular/core';
+import {Observable, Observer, of} from 'rxjs';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {AuthService} from '@eg/core/auth.service';
+import {NetService} from '@eg/core/net.service';
+import {EventService} from '@eg/core/event.service';
+import {OrgService} from '@eg/core/org.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {Pager} from '@eg/share/util/pager';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {CourseService} from './course.service';
+
+@Component({
+    selector: 'eg-course-associate-material-dialog',
+    templateUrl: './course-associate-material.component.html'
+})
+
+export class CourseAssociateMaterialComponent extends DialogComponent {
+
+    @ViewChild('materialsGrid', {static: true}) materialsGrid: GridComponent;
+    @ViewChild('deleteFailedString', { static: true }) deleteFailedString: StringComponent;
+    @ViewChild('deleteSuccessString', { static: true }) deleteSuccessString: StringComponent;
+    @ViewChild('successString', { static: true }) successString: StringComponent;
+    @ViewChild('failedString', { static: true }) failedString: StringComponent;
+    @ViewChild('differentLibraryString', { static: true }) differentLibraryString: StringComponent;
+    @Input() table_name = "Course Materials";
+    @Input() barcodeInput: String;
+    @Input() relationshipInput: String;
+    @Input() tempCallNumber: String;
+    @Input() tempStatus: Number;
+    @Input() tempLocation: Number;
+    @Input() tempCircMod: String;
+    @Input() isModifyingStatus: Boolean;
+    @Input() isModifyingCircMod: Boolean;
+    @Input() isModifyingCallNumber: Boolean;
+    @Input() isModifyingLocation: Boolean;
+    currentCourse: IdlObject;
+    materials: any[];
+    gridDataSource: GridDataSource;
+
+    constructor(
+        private auth: AuthService,
+        private idl: IdlService,
+        private net: NetService,
+        private pcrud: PcrudService,
+        private org: OrgService,
+        private evt: EventService,
+        private modal: NgbModal,
+        private toast: ToastService,
+        private courseSvc: CourseService
+    ) {
+        super(modal);
+        this.gridDataSource = new GridDataSource();
+    }
+
+    ngOnInit() {
+        this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
+            return this.fetchMaterials(pager);
+        }
+    }
+
+    deleteSelected(items) {
+        let item_ids = [];
+        items.forEach(item => {
+            this.gridDataSource.data.splice(this.gridDataSource.data.indexOf(item, 0), 1);
+            item_ids.push(item.id())
+        });
+        this.pcrud.search('acmcm', {course: this.currentCourse.id(), item: item_ids}).subscribe(material => {
+            material.isdeleted(true);
+            this.pcrud.autoApply(material).subscribe(
+                val => {
+                    this.courseSvc.resetItemFields(material, this.currentCourse.owning_lib());
+                    console.debug('deleted: ' + val);
+                    this.deleteSuccessString.current().then(str => this.toast.success(str));
+                },
+                err => {
+                    this.deleteFailedString.current()
+                        .then(str => this.toast.danger(str));
+                }
+            );
+        });
+    }
+
+    associateItem(barcode, relationship) {
+        if (barcode) {
+            this.pcrud.search('acp', {barcode: barcode},
+              {flesh: 3, flesh_fields: {acp: ['call_number']}}).subscribe(item => {
+                let material = this.idl.create('acmcm');
+                material.item(item.id());
+                material.course(this.currentCourse.id());
+                if (relationship) material.relationship(relationship);
+                if (this.isModifyingStatus && this.tempStatus) {
+                    material.original_status(item.status());
+                    item.status(this.tempStatus);
+                }
+                if (this.isModifyingLocation && this.tempLocation) {
+                    material.original_location(item.location());
+                    item.location(this.tempLocation);
+                }
+                if (this.isModifyingCircMod) {
+                    material.original_circ_modifier(item.circ_modifier());
+                    item.circ_modifier(this.tempCircMod);
+                    if (!this.tempCircMod) item.circ_modifier(null);
+                }
+                if (this.isModifyingCallNumber) {
+                    material.original_callnumber(item.call_number());
+                }
+                this.pcrud.create(material).subscribe(
+                val => {
+                   console.debug('created: ' + val);
+                   let new_cn = item.call_number().label();
+                   if (this.tempCallNumber) new_cn = this.tempCallNumber;
+                    this.courseSvc.updateItem(item, this.currentCourse.owning_lib(), new_cn, this.isModifyingCallNumber).then(res => {
+                        this.fetchItem(item.id(), relationship);                        
+                        if (item.circ_lib() != this.currentCourse.owning_lib()) {
+                            this.differentLibraryString.current().then(str => this.toast.warning(str));
+                        } else {
+                            this.successString.current().then(str => this.toast.success(str));
+                        }
+                    });
+
+                    // Cleaning up inputs
+                    this.barcodeInput = "";
+                    this.relationshipInput = "";
+                    this.tempStatus = null;
+                    this.tempCircMod = null;
+                    this.tempCallNumber = null;
+                    this.tempLocation = null;
+                    this.isModifyingCallNumber = false;
+                    this.isModifyingCircMod = false;
+                    this.isModifyingLocation = false;
+                    this.isModifyingStatus = false;
+                }, err => {
+                    this.failedString.current().then(str => this.toast.danger(str));
+                });
+            });
+        }
+    }
+
+    fetchMaterials(pager: Pager): Observable<any> {
+        return new Observable<any>(observer => {
+            this.materials.forEach(material => {
+                this.fetchItem(material.item, material.relationship);
+            });
+            observer.complete();
+        });
+    }
+
+    fetchItem(itemId, relationship): Promise<any> {
+        return new Promise((resolve, reject) => {
+            this.net.request(
+                'open-ils.circ',
+                'open-ils.circ.copy_details.retrieve',
+                this.auth.token(), itemId
+            ).subscribe(res => {
+                if (res) {
+                    let item = res.copy;
+                    item.call_number(res.volume);
+                    item.circ_lib(this.org.get(item.circ_lib()));
+                    item._title = res.mvr.title();
+                    item._relationship = relationship;
+                    this.gridDataSource.data.push(item);
+                }
+            }, err => {
+                reject(err);
+            }, () => resolve(this.gridDataSource.data));
+        });
+    }
+}
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-list.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-list.component.html
new file mode 100644 (file)
index 0000000..d31f440
--- /dev/null
@@ -0,0 +1,36 @@
+<eg-staff-banner bannerText="Course List" i18n-bannerText>
+</eg-staff-banner>
+
+<eg-string #successString i18n-text text="{{table_name}} Update Succeeded"></eg-string>
+<eg-string #createString i18n-text text="{{table_name}} Was Created Successfully"></eg-string>
+<eg-string #deleteFailedString i18n-text text="Delete of {{table_name}} failed or was not allowed"></eg-string>
+<eg-string #deleteSucailedString i18n-text text="Delete of {{table_name}} failed or was not allowed"></eg-string>
+<eg-string #archiveFailedString i18n-text text="Archival of {{table_name}} failed or was not allowed"></eg-string>
+<eg-string #archiveSuccessString i18n-text text="Archival of {{table_name}} succeeded"></eg-string>
+<eg-string #flairTooltip i18n-text text="Limited Editing"></eg-string>
+
+<eg-course-associate-material-dialog #courseMaterialDialog>
+</eg-course-associate-material-dialog>
+<div class="w-100 mt-2 mb-2">
+  <eg-grid #grid idlClass={{idl_class}}
+    [dataSource]="grid_source"
+    [sortable]="true">
+    <eg-grid-toolbar-button
+      label="Create {{table_name}}" (onClick)="createNew()" i18n-label>
+    </eg-grid-toolbar-button>
+    <eg-grid-toolbar-action label="View Materials" i18n-label (onClick)="openMaterialsDialog($event)">
+    </eg-grid-toolbar-action>
+    <eg-grid-toolbar-action label="Edit Selected" i18n-label (onClick)="editSelected($event)">
+    </eg-grid-toolbar-action>
+    <eg-grid-toolbar-action label="Delete Selected" i18n-label (onClick)="deleteSelected($event)">
+    </eg-grid-toolbar-action>
+    <eg-grid-toolbar-action label="Archive Selected" i18n-label (onClick)="archiveSelected($event)">
+    </eg-grid-toolbar-action>
+  </eg-grid>
+</div>
+
+<eg-fm-record-editor #editDialog
+  idlClass={{idl_class}}
+  [preloadLinkedValues]="true"
+  hiddenFields="is_archived,id">
+</eg-fm-record-editor>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-list.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-list.component.ts
new file mode 100644 (file)
index 0000000..ab9cd3b
--- /dev/null
@@ -0,0 +1,212 @@
+import {Component, Input, ViewChild, OnInit} from '@angular/core';
+import {IdlObject} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {AuthService} from '@eg/core/auth.service';
+import {CourseService} from './course.service';
+import {NetService} from '@eg/core/net.service';
+import {OrgService} from '@eg/core/org.service';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {Pager} from '@eg/share/util/pager';
+import {GridDataSource, GridColumn} from '@eg/share/grid/grid';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+
+import {CourseAssociateMaterialComponent
+    } from './course-associate-material.component';
+
+@Component({
+    templateUrl: './course-list.component.html'
+})
+
+export class CourseListComponent implements OnInit { 
+    @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+    @ViewChild('grid', { static: true }) grid: GridComponent;
+    @ViewChild('successString', { static: true }) successString: StringComponent;
+    @ViewChild('createString', { static: false }) createString: StringComponent;
+    @ViewChild('createErrString', { static: false }) createErrString: StringComponent;
+    @ViewChild('updateFailedString', { static: false }) updateFailedString: StringComponent;
+    @ViewChild('deleteFailedString', { static: true }) deleteFailedString: StringComponent;
+    @ViewChild('deleteSuccessString', { static: true }) deleteSuccessString: StringComponent;
+    @ViewChild('archiveFailedString', { static: true }) archiveFailedString: StringComponent;
+    @ViewChild('archiveSuccessString', { static: true }) archiveSuccessString: StringComponent;
+    @ViewChild('courseMaterialDialog', {static: true})
+        private courseMaterialDialog: CourseAssociateMaterialComponent;
+    @Input() sort_field: string;
+    @Input() idl_class = "acmc";
+    @Input() dialog_size: 'sm' | 'lg' = 'lg';
+    @Input() table_name = "Course";
+    grid_source: GridDataSource = new GridDataSource();
+    currentMaterials: any[] = [];
+    search_value = '';
+
+    constructor(
+        private auth: AuthService,
+        private courseSvc: CourseService,
+        private net: NetService,
+        private org: OrgService,
+        private pcrud: PcrudService,
+        private toast: ToastService,
+    ){}
+
+    ngOnInit() {
+        this.getSource();
+    }
+
+    /**
+     * Gets the data, specified by the class, that is available.
+     */
+    getSource() {
+        this.grid_source.getRows = (pager: Pager, sort: any[]) => {
+            const orderBy: any = {};
+            if (sort.length) {
+                // Sort specified from grid
+                orderBy[this.idl_class] = sort[0].name + ' ' + sort[0].dir;
+            } else if (this.sort_field) {
+                // Default sort field
+                orderBy[this.idl_class] = this.sort_field;
+            }
+            const searchOps = {
+                offset: pager.offset,
+                limit: pager.limit,
+                order_by: orderBy
+            };
+            return this.pcrud.retrieveAll(this.idl_class, searchOps, {fleshSelectors: true});
+        };
+    }
+
+    showEditDialog(standingPenalty: IdlObject): Promise<any> {
+        this.editDialog.mode = 'update';
+        this.editDialog.recordId = standingPenalty['id']();
+        return new Promise((resolve, reject) => {
+            this.editDialog.open({size: this.dialog_size}).subscribe(
+                result => {
+                    this.successString.current()
+                        .then(str => this.toast.success(str));
+                    this.grid.reload();
+                    resolve(result);
+                },
+                error => {
+                    this.updateFailedString.current()
+                        .then(str => this.toast.danger(str));
+                    reject(error);
+                }
+            );
+        });
+    }
+
+    createNew() {
+        this.editDialog.mode = 'create';
+        this.editDialog.recordId = null;
+        this.editDialog.record = null;
+        this.editDialog.open({size: this.dialog_size}).subscribe(
+            ok => {
+                this.createString.current()
+                    .then(str => this.toast.success(str));
+                this.grid.reload();
+            },
+            rejection => {
+                if (!rejection.dismissed) {
+                    this.createErrString.current()
+                        .then(str => this.toast.danger(str));
+                }
+            }
+        );
+    }
+
+    editSelected(fields: IdlObject[]) {
+        // Edit each IDL thing one at a time
+        const editOneThing = (field_object: IdlObject) => {
+            if (!field_object) { return; }
+            this.showEditDialog(field_object).then(
+                () => editOneThing(fields.shift()));
+        };
+        editOneThing(fields.shift());
+    }
+
+    archiveSelected(course: IdlObject[]) {
+        this.courseSvc.disassociateMaterials(course).then(res => {
+            course.forEach(course => {
+                console.log(course);
+                course.is_archived(true);
+            });
+            this.pcrud.update(course).subscribe(
+                val => {
+                    console.debug('archived: ' + val);
+                    this.archiveSuccessString.current()
+                        .then(str => this.toast.success(str));
+                }, err => {
+                    this.archiveFailedString.current()
+                        .then(str => this.toast.danger(str));
+                }, () => {
+                    this.grid.reload();
+                }
+            );
+        });
+    }
+
+    deleteSelected(idl_object: IdlObject[]) {
+        this.courseSvc.disassociateMaterials(idl_object).then(res => {
+            idl_object.forEach(idl_object => {
+                idl_object.isdeleted(true)
+            });
+            this.pcrud.autoApply(idl_object).subscribe(
+                val => {
+                    console.debug('deleted: ' + val);
+                    this.deleteSuccessString.current()
+                        .then(str => this.toast.success(str));
+                },
+                err => {
+                    this.deleteFailedString.current()
+                        .then(str => this.toast.danger(str));
+                },
+                () => this.grid.reload()
+            );
+        });
+    };
+
+    fetchCourseMaterials(course, currentMaterials): Promise<any> {
+        return new Promise((resolve, reject) => {
+            this.pcrud.search('acmcm', {course: course}).subscribe(res => {
+                if (res) this.fleshItemDetails(res.item(), res.relationship());
+            }, err => {
+                reject(err);
+            }, () => resolve(this.courseMaterialDialog.gridDataSource.data));
+        });
+    }
+
+    fleshItemDetails(itemId, relationship): Promise<any> {
+        return new Promise((resolve, reject) => {
+            this.net.request(
+                'open-ils.circ',
+                'open-ils.circ.copy_details.retrieve',
+                this.auth.token(), itemId
+            ).subscribe(res => {
+                if (res) {
+                    let item = res.copy;
+                    item.call_number(res.volume);
+                    item._title = res.mvr.title();
+                    item.circ_lib(this.org.get(item.circ_lib()));
+                    item._relationship = relationship;
+                    this.courseMaterialDialog.gridDataSource.data.push(item);
+                }
+            }, err => {
+                reject(err);
+            }, () => resolve(this.courseMaterialDialog.gridDataSource.data));
+        });
+    }
+
+    openMaterialsDialog(course) {
+        let currentMaterials = []
+        this.courseMaterialDialog.gridDataSource.data = [];
+        this.fetchCourseMaterials(course[0].id(), currentMaterials).then(res => {
+            this.courseMaterialDialog.currentCourse = course[0];
+            this.courseMaterialDialog.materials = currentMaterials;
+            this.courseMaterialDialog.open({size: 'lg'}).subscribe(res => {
+                console.log(res);
+            });
+        });
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-reserves.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-reserves.module.ts
new file mode 100644 (file)
index 0000000..6f6eed5
--- /dev/null
@@ -0,0 +1,28 @@
+import {NgModule} from '@angular/core';
+import {TreeModule} from '@eg/share/tree/tree.module';
+import {AdminCommonModule} from '@eg/staff/admin/common.module';
+import {CourseListComponent} from './course-list.component';
+import {CourseAssociateMaterialComponent} from './course-associate-material.component';
+import {CourseReservesRoutingModule} from './routing.module';
+import {ItemLocationSelectModule} from '@eg/share/item-location-select/item-location-select.module';
+import {CourseService} from './course.service'
+@NgModule({
+  declarations: [
+    CourseListComponent,
+    CourseAssociateMaterialComponent
+  ],
+  imports: [
+    AdminCommonModule,
+    CourseReservesRoutingModule,
+    ItemLocationSelectModule,
+    TreeModule
+  ],
+  exports: [
+  ],
+  providers: [
+    CourseService
+  ]
+})
+
+export class CourseReservesModule {
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course.service.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course.service.ts
new file mode 100644 (file)
index 0000000..c8b60cc
--- /dev/null
@@ -0,0 +1,100 @@
+import {AuthService} from '@eg/core/auth.service';
+import {EventService} from '@eg/core/event.service';
+import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+
+export class CourseService {
+
+    constructor(
+        private auth: AuthService,
+        private evt: EventService,
+        private idl: IdlService,
+        private net: NetService,
+        private pcrud: PcrudService
+    ) {}
+
+    disassociateMaterials(courses) {
+        return new Promise((resolve, reject) => {
+            let course_ids = [];
+            let course_library_hash = {};
+            courses.forEach(course => {
+                course_ids.push(course.id());
+                course_library_hash[course.id()] = course.owning_lib();
+            });
+            this.pcrud.search('acmcm', {course: course_ids}).subscribe(material => {
+                material.isdeleted(true);
+                this.resetItemFields(material, course_library_hash[material.course()]);
+                this.pcrud.autoApply(material).subscribe(res => {
+                    console.log(res);
+                }, err => {
+                    reject(err);
+                }, () => {
+                    resolve(material);
+                });
+            }, err => {
+                reject(err)
+            }, () => {
+                resolve(courses);
+            });
+        });
+    }
+
+    resetItemFields(material, course_lib) {
+        this.pcrud.retrieve('acp', material.item(),
+            {flesh: 3, flesh_fields: {acp: ['call_number']}}).subscribe(copy => {
+            if (material.original_status()) {
+                copy.status(material.original_status());
+            }
+            if (copy.circ_modifier() != material.original_circ_modifier()) {
+                copy.circ_modifier(material.original_circ_modifier());
+            }
+            if (material.original_location()) {
+                copy.location(material.original_location());
+            }
+            if (material.original_callnumber()) {
+                this.pcrud.retrieve('acn', material.original_callnumber()).subscribe(cn => {
+                    this.updateItem(copy, course_lib, cn.label(), true);
+                });
+            } else {
+                this.updateItem(copy, course_lib, copy.call_number().label(), false);
+            }
+        });
+    }
+
+    updateItem(item: IdlObject, course_lib, call_number, updatingVolume) {
+        return new Promise((resolve, reject) => {
+            this.pcrud.update(item).subscribe(item_id => {
+                if (updatingVolume) {
+                    let cn = item.call_number();
+                    return this.net.request(
+                        'open-ils.cat', 'open-ils.cat.call_number.find_or_create',
+                        this.auth.token(), call_number, cn.record(),
+                        course_lib, cn.prefix(), cn.suffix(),
+                        cn.label_class()
+                    ).subscribe(res => {
+                        let event = this.evt.parse(res);
+                        if (event) return;
+                        return this.net.request(
+                            'open-ils.cat', 'open-ils.cat.transfer_copies_to_volume',
+                            this.auth.token(), res.acn_id, [item.id()]
+                        ).subscribe(transfered_res => {
+                            console.debug("Copy transferred to volume with code " + transfered_res);
+                        }, err => {
+                            reject(err);
+                        }, () => {
+                            resolve(item);
+                        });
+                    }, err => {
+                        reject(err);
+                    }, () => {
+                        resolve(item);
+                    });
+                } else {
+                    return this.pcrud.update(item);
+                }
+            });
+        });
+    }
+
+}
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/routing.module.ts
new file mode 100644 (file)
index 0000000..84e74ff
--- /dev/null
@@ -0,0 +1,15 @@
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {CourseListComponent} from './course-list.component';
+
+const routes: Routes = [{
+    path: '',
+    component: CourseListComponent
+}];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+
+export class CourseReservesRoutingModule {}
\ No newline at end of file
index c29e7e6..e0e5414 100644 (file)
@@ -20,6 +20,9 @@ const routes: Routes = [{
     path: 'container/carousel',
     component: AdminCarouselComponent
 }, {
+    path: 'asset/course_list',
+    loadChildren: '@eg/staff/admin/local/course-reserves/course-reserves.module#CourseReservesModule'
+}, {
     path: 'config/standing_penalty',
     component: StandingPenaltyComponent
 }, {
index df5b9ef..de399d6 100644 (file)
@@ -39,8 +39,6 @@
       routerLink="/staff/admin/server/config/circ_modifier"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Circulation Recurring Fine Rules"  
       routerLink="/staff/admin/server/config/rule_recurring_fine"></eg-link-table-link>
-    <eg-link-table-link i18n-label label="Course Reserves List"  
-      routerLink="/staff/admin/server/asset/course_list"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Custom Org Unit Trees"  
       url="/eg/staff/admin/server/actor/org_unit_custom_tree"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Floating Groups"  
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-associate-material.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-associate-material.component.html
deleted file mode 100644 (file)
index 488eea7..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-<eg-string #deleteFailedString i18n-text text="Disassociation of Course Material failed or was not allowed"></eg-string>
-<eg-string #deleteSuccessString i18n-text text="Disassociation of Course Material succeeded"></eg-string>
-<eg-string #successString i18n-text text="Association of Course Material succeeded"></eg-string>
-<eg-string #failedString i18n-text text="Association of Course Material failed or was not allowed"></eg-string>
-<eg-string #differentLibraryString i18n-text text="Material exists at a different library"></eg-string>
-
-<ng-template #dialogContent>
-  <div class="modal-header bg-info">
-    <h4 class="modal-title" i18n>Course Materials</h4>
-    <button type="button" class="close"
-      i18n-aria-label aria-label="Close" (click)="close()">
-      <span aria-hidden="true">&times;</span>
-    </button>
-  </div>
-  <div class="modal-body">
-    <div class="row mt-3">
-      <div class="col-md-4">
-        <div class="input-group">
-          <div class="input-group-prepend">
-            <span class="input-group-text" i18n>Barcode</span>
-          </div>
-          <input type="text" [(ngModel)]="barcodeInput" />
-        </div>
-      </div>
-      <div class="col-md-5">
-        <div class="input-group">
-          <div class="input-group-prepend">
-            <span class="input-group-text" i18n>Relationship</span>
-          </div>
-          <input type="text" [(ngModel)]="relationshipInput" placeholder-i18n placeholder="e.g. Required" />
-        </div>
-      </div>
-      <div class="col-md-3">
-        <button class="btn btn-outline-dark" (click)="associateItem(barcodeInput, relationshipInput)" i18n [disabled]="!barcodeInput">Add Material</button>
-      </div>
-    </div>
-    <div class="row justify-content-center mt-3">
-      <div class="col">
-        <h5 i18n>The following fields will be applied to the material added, and reverted once the course is no longer associated with the material.</h5>
-      </div>
-    </div>
-    <div class="row mt-3">
-      <div class="col-md-6">
-        <div class="input-group">
-          <div class="input-group-prepend">
-            <div class="input-group-text">
-              <span i18n>Call Number</span>
-            </div>
-          </div>
-          <input type="text" [(ngModel)]="tempCallNumber"
-            (input)="isModifyingCallNumber = true"/>
-          <div class="input-group-append">
-            <div class="input-group-text">
-              <input type="checkbox" [(ngModel)]="isModifyingCallNumber"
-                aria-label="Checkbox for setting a temporary Call Number" />
-            </div>
-          </div>
-        </div>
-      </div>
-      <div class="col-md-6">
-        <div class="input-group">
-          <div class="input-group-prepend">
-            <div class="input-group-text">
-              <span i18n>Circulation Modifier</span>
-            </div>
-          </div>
-          <eg-combobox i18n-placeholder placeholder="Circulation Modifier..."
-            idlClass="ccm" idlField="name" [displayTemplate]="idlClassLabel"
-            [asyncSupportsEmptyTermClick]="true"
-            (onChange)="tempCircMod = $event.id; isModifyingCircMod = true">
-          </eg-combobox>
-          <div class="input-group-append">
-            <div class="input-group-text">
-              <input type="checkbox" [(ngModel)]="isModifyingCircMod"
-                aria-label="Checkbox for setting a temporary Circulation Modifier" />
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div class="row mt-3">
-      <div class="col-md-6">
-        <div class="input-group">
-          <div class="input-group-prepend">
-            <div class="input-group-text">
-              <span i18n>Item Status</span>
-            </div>
-          </div>
-          <eg-combobox i18n-placeholder placeholder="Item Status..."
-            idlClass="ccs" idlField="name" [displayTemplate]="idlClassLabel"
-            [asyncSupportsEmptyTermClick]="true"
-            (onChange)="tempStatus = $event.id; isModifyingStatus = true">
-          </eg-combobox>
-          <div class="input-group-append">
-            <div class="input-group-text">
-              <input type="checkbox" [(ngModel)]="isModifyingStatus"
-                aria-label="Checkbox for setting a temporary Item Status" />
-            </div>
-          </div>
-        </div>
-      </div>
-      <div class="col-md-6">
-        <div class="input-group">
-          <div class="input-group-prepend">
-            <div class="input-group-text">
-              <span i18n>Shelving Location</span>
-            </div>
-          </div>
-          <eg-item-location-select permFilter="MANAGE_RESERVES"
-            [(ngModel)]="tempLocation" (oninput)="isModifyingLocation = true">
-          </eg-item-location-select>
-          <div class="input-group-append">
-            <div class="input-group-text">
-              <input type="checkbox" [(ngModel)]="isModifyingLocation"
-                aria-label="Checkbox for setting a temporary Shelving Location" />
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <div class="mt-3">
-      <eg-grid #materialGrid [dataSource]="gridDataSource">
-        <eg-grid-toolbar-action label="Delete Selected" i18n-label (onClick)="deleteSelected($event)">
-        </eg-grid-toolbar-action>
-
-        <eg-grid-column path="id" [index]=true [hidden]="true" label="ID" i18n-label></eg-grid-column>
-        <eg-grid-column label="Barcode" i18n-label name="barcode" [cellTemplate]="barcodeCellTemplate"></eg-grid-column>
-        <eg-grid-column label="Title" i18n-label name="title" [cellTemplate]="titleCellTemplate"></eg-grid-column>
-        <eg-grid-column path="call_number.label" label="Call Number" i18n-label></eg-grid-column>
-        <eg-grid-column path="call_number.prefix.label" [hidden]="true" label="Call Number Prefix" i18n-label hidden></eg-grid-column>
-        <eg-grid-column path="call_number.suffix.label" [hidden]="true" label="Call Number Suffix" i18n-label hidden></eg-grid-column>
-        <eg-grid-column path="circ_modifier" [hidden]="true" label="Circulation Modifier" i18n-label></eg-grid-column>
-        <eg-grid-column path="circ_lib.shortname" label="Circulation Library" i18n-label></eg-grid-column>
-        <eg-grid-column path="location.name" [hidden]="true" label="Shelving Location" i18n-label></eg-grid-column>
-        <eg-grid-column path="status.name" [hidden]="true" label="Copy Status" i18n-label></eg-grid-column>
-        <eg-grid-column path="_relationship" label="Relationship" i18n-label></eg-grid-column>
-      </eg-grid>
-    </div>
-  </div>
-  <ng-template #barcodeCellTemplate let-entry="row">
-    <span>
-      <a class="pl-1"
-        href="/eg/staff/cat/item/{{entry.id()}}">
-        {{entry.barcode()}}
-      </a>
-    </span>
-  </ng-template>
-  <ng-template #titleCellTemplate let-entry="row">
-    <span>
-      <a class="pl-1"
-        href="/eg/staff/cat/catalog/record/{{entry.call_number().record()}}">
-        {{entry._title}}
-      </a>
-    </span>
-  </ng-template>
-  <ng-template #idlClassLabel let-r="result" i18n>
-    {{r.label}}
-  </ng-template>
-</ng-template>
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-associate-material.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-associate-material.component.ts
deleted file mode 100644 (file)
index cb8eeeb..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-import {Component, Input, ViewChild, OnInit, TemplateRef} from '@angular/core';
-import {Observable, Observer, of} from 'rxjs';
-import {DialogComponent} from '@eg/share/dialog/dialog.component';
-import {AuthService} from '@eg/core/auth.service';
-import {NetService} from '@eg/core/net.service';
-import {EventService} from '@eg/core/event.service';
-import {OrgService} from '@eg/core/org.service';
-import {PcrudService} from '@eg/core/pcrud.service';
-import {Pager} from '@eg/share/util/pager';
-import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
-import {GridDataSource} from '@eg/share/grid/grid';
-import {GridComponent} from '@eg/share/grid/grid.component';
-import {IdlObject, IdlService} from '@eg/core/idl.service';
-import {StringComponent} from '@eg/share/string/string.component';
-import {ToastService} from '@eg/share/toast/toast.service';
-import {CourseService} from './course.service';
-
-@Component({
-    selector: 'eg-course-associate-material-dialog',
-    templateUrl: './course-associate-material.component.html'
-})
-
-export class CourseAssociateMaterialComponent extends DialogComponent {
-
-    @ViewChild('materialsGrid', {static: true}) materialsGrid: GridComponent;
-    @ViewChild('deleteFailedString', { static: true }) deleteFailedString: StringComponent;
-    @ViewChild('deleteSuccessString', { static: true }) deleteSuccessString: StringComponent;
-    @ViewChild('successString', { static: true }) successString: StringComponent;
-    @ViewChild('failedString', { static: true }) failedString: StringComponent;
-    @ViewChild('differentLibraryString', { static: true }) differentLibraryString: StringComponent;
-    @Input() table_name = "Course Materials";
-    @Input() barcodeInput: String;
-    @Input() relationshipInput: String;
-    @Input() tempCallNumber: String;
-    @Input() tempStatus: Number;
-    @Input() tempLocation: Number;
-    @Input() tempCircMod: String;
-    @Input() isModifyingStatus: Boolean;
-    @Input() isModifyingCircMod: Boolean;
-    @Input() isModifyingCallNumber: Boolean;
-    @Input() isModifyingLocation: Boolean;
-    currentCourse: IdlObject;
-    materials: any[];
-    gridDataSource: GridDataSource;
-
-    constructor(
-        private auth: AuthService,
-        private idl: IdlService,
-        private net: NetService,
-        private pcrud: PcrudService,
-        private org: OrgService,
-        private evt: EventService,
-        private modal: NgbModal,
-        private toast: ToastService,
-        private courseSvc: CourseService
-    ) {
-        super(modal);
-        this.gridDataSource = new GridDataSource();
-    }
-
-    ngOnInit() {
-        this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
-            return this.fetchMaterials(pager);
-        }
-    }
-
-    deleteSelected(items) {
-        let item_ids = [];
-        items.forEach(item => {
-            this.gridDataSource.data.splice(this.gridDataSource.data.indexOf(item, 0), 1);
-            item_ids.push(item.id())
-        });
-        this.pcrud.search('acmcm', {course: this.currentCourse.id(), item: item_ids}).subscribe(material => {
-            material.isdeleted(true);
-            this.pcrud.autoApply(material).subscribe(
-                val => {
-                    this.courseSvc.resetItemFields(material, this.currentCourse.owning_lib());
-                    console.debug('deleted: ' + val);
-                    this.deleteSuccessString.current().then(str => this.toast.success(str));
-                },
-                err => {
-                    this.deleteFailedString.current()
-                        .then(str => this.toast.danger(str));
-                }
-            );
-        });
-    }
-
-    associateItem(barcode, relationship) {
-        if (barcode) {
-            this.pcrud.search('acp', {barcode: barcode},
-              {flesh: 3, flesh_fields: {acp: ['call_number']}}).subscribe(item => {
-                let material = this.idl.create('acmcm');
-                material.item(item.id());
-                material.course(this.currentCourse.id());
-                if (relationship) material.relationship(relationship);
-                if (this.isModifyingStatus && this.tempStatus) {
-                    material.original_status(item.status());
-                    item.status(this.tempStatus);
-                }
-                if (this.isModifyingLocation && this.tempLocation) {
-                    material.original_location(item.location());
-                    item.location(this.tempLocation);
-                }
-                if (this.isModifyingCircMod) {
-                    material.original_circ_modifier(item.circ_modifier());
-                    item.circ_modifier(this.tempCircMod);
-                    if (!this.tempCircMod) item.circ_modifier(null);
-                }
-                if (this.isModifyingCallNumber) {
-                    material.original_callnumber(item.call_number());
-                }
-                this.pcrud.create(material).subscribe(
-                val => {
-                   console.debug('created: ' + val);
-                   let new_cn = item.call_number().label();
-                   if (this.tempCallNumber) new_cn = this.tempCallNumber;
-                    this.courseSvc.updateItem(item, this.currentCourse.owning_lib(), new_cn, this.isModifyingCallNumber).then(res => {
-                        this.fetchItem(item.id(), relationship);                        
-                        if (item.circ_lib() != this.currentCourse.owning_lib()) {
-                            this.differentLibraryString.current().then(str => this.toast.warning(str));
-                        } else {
-                            this.successString.current().then(str => this.toast.success(str));
-                        }
-                    });
-
-                    // Cleaning up inputs
-                    this.barcodeInput = "";
-                    this.relationshipInput = "";
-                    this.tempStatus = null;
-                    this.tempCircMod = null;
-                    this.tempCallNumber = null;
-                    this.tempLocation = null;
-                    this.isModifyingCallNumber = false;
-                    this.isModifyingCircMod = false;
-                    this.isModifyingLocation = false;
-                    this.isModifyingStatus = false;
-                }, err => {
-                    this.failedString.current().then(str => this.toast.danger(str));
-                });
-            });
-        }
-    }
-
-    fetchMaterials(pager: Pager): Observable<any> {
-        return new Observable<any>(observer => {
-            this.materials.forEach(material => {
-                this.fetchItem(material.item, material.relationship);
-            });
-            observer.complete();
-        });
-    }
-
-    fetchItem(itemId, relationship): Promise<any> {
-        return new Promise((resolve, reject) => {
-            this.net.request(
-                'open-ils.circ',
-                'open-ils.circ.copy_details.retrieve',
-                this.auth.token(), itemId
-            ).subscribe(res => {
-                if (res) {
-                    let item = res.copy;
-                    item.call_number(res.volume);
-                    item.circ_lib(this.org.get(item.circ_lib()));
-                    item._title = res.mvr.title();
-                    item._relationship = relationship;
-                    this.gridDataSource.data.push(item);
-                }
-            }, err => {
-                reject(err);
-            }, () => resolve(this.gridDataSource.data));
-        });
-    }
-}
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-list.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-list.component.html
deleted file mode 100644 (file)
index d31f440..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<eg-staff-banner bannerText="Course List" i18n-bannerText>
-</eg-staff-banner>
-
-<eg-string #successString i18n-text text="{{table_name}} Update Succeeded"></eg-string>
-<eg-string #createString i18n-text text="{{table_name}} Was Created Successfully"></eg-string>
-<eg-string #deleteFailedString i18n-text text="Delete of {{table_name}} failed or was not allowed"></eg-string>
-<eg-string #deleteSucailedString i18n-text text="Delete of {{table_name}} failed or was not allowed"></eg-string>
-<eg-string #archiveFailedString i18n-text text="Archival of {{table_name}} failed or was not allowed"></eg-string>
-<eg-string #archiveSuccessString i18n-text text="Archival of {{table_name}} succeeded"></eg-string>
-<eg-string #flairTooltip i18n-text text="Limited Editing"></eg-string>
-
-<eg-course-associate-material-dialog #courseMaterialDialog>
-</eg-course-associate-material-dialog>
-<div class="w-100 mt-2 mb-2">
-  <eg-grid #grid idlClass={{idl_class}}
-    [dataSource]="grid_source"
-    [sortable]="true">
-    <eg-grid-toolbar-button
-      label="Create {{table_name}}" (onClick)="createNew()" i18n-label>
-    </eg-grid-toolbar-button>
-    <eg-grid-toolbar-action label="View Materials" i18n-label (onClick)="openMaterialsDialog($event)">
-    </eg-grid-toolbar-action>
-    <eg-grid-toolbar-action label="Edit Selected" i18n-label (onClick)="editSelected($event)">
-    </eg-grid-toolbar-action>
-    <eg-grid-toolbar-action label="Delete Selected" i18n-label (onClick)="deleteSelected($event)">
-    </eg-grid-toolbar-action>
-    <eg-grid-toolbar-action label="Archive Selected" i18n-label (onClick)="archiveSelected($event)">
-    </eg-grid-toolbar-action>
-  </eg-grid>
-</div>
-
-<eg-fm-record-editor #editDialog
-  idlClass={{idl_class}}
-  [preloadLinkedValues]="true"
-  hiddenFields="is_archived,id">
-</eg-fm-record-editor>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-list.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-list.component.ts
deleted file mode 100644 (file)
index ab9cd3b..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-import {Component, Input, ViewChild, OnInit} from '@angular/core';
-import {IdlObject} from '@eg/core/idl.service';
-import {PcrudService} from '@eg/core/pcrud.service';
-import {AuthService} from '@eg/core/auth.service';
-import {CourseService} from './course.service';
-import {NetService} from '@eg/core/net.service';
-import {OrgService} from '@eg/core/org.service';
-import {GridComponent} from '@eg/share/grid/grid.component';
-import {Pager} from '@eg/share/util/pager';
-import {GridDataSource, GridColumn} from '@eg/share/grid/grid';
-import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
-import {StringComponent} from '@eg/share/string/string.component';
-import {ToastService} from '@eg/share/toast/toast.service';
-
-import {CourseAssociateMaterialComponent
-    } from './course-associate-material.component';
-
-@Component({
-    templateUrl: './course-list.component.html'
-})
-
-export class CourseListComponent implements OnInit { 
-    @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
-    @ViewChild('grid', { static: true }) grid: GridComponent;
-    @ViewChild('successString', { static: true }) successString: StringComponent;
-    @ViewChild('createString', { static: false }) createString: StringComponent;
-    @ViewChild('createErrString', { static: false }) createErrString: StringComponent;
-    @ViewChild('updateFailedString', { static: false }) updateFailedString: StringComponent;
-    @ViewChild('deleteFailedString', { static: true }) deleteFailedString: StringComponent;
-    @ViewChild('deleteSuccessString', { static: true }) deleteSuccessString: StringComponent;
-    @ViewChild('archiveFailedString', { static: true }) archiveFailedString: StringComponent;
-    @ViewChild('archiveSuccessString', { static: true }) archiveSuccessString: StringComponent;
-    @ViewChild('courseMaterialDialog', {static: true})
-        private courseMaterialDialog: CourseAssociateMaterialComponent;
-    @Input() sort_field: string;
-    @Input() idl_class = "acmc";
-    @Input() dialog_size: 'sm' | 'lg' = 'lg';
-    @Input() table_name = "Course";
-    grid_source: GridDataSource = new GridDataSource();
-    currentMaterials: any[] = [];
-    search_value = '';
-
-    constructor(
-        private auth: AuthService,
-        private courseSvc: CourseService,
-        private net: NetService,
-        private org: OrgService,
-        private pcrud: PcrudService,
-        private toast: ToastService,
-    ){}
-
-    ngOnInit() {
-        this.getSource();
-    }
-
-    /**
-     * Gets the data, specified by the class, that is available.
-     */
-    getSource() {
-        this.grid_source.getRows = (pager: Pager, sort: any[]) => {
-            const orderBy: any = {};
-            if (sort.length) {
-                // Sort specified from grid
-                orderBy[this.idl_class] = sort[0].name + ' ' + sort[0].dir;
-            } else if (this.sort_field) {
-                // Default sort field
-                orderBy[this.idl_class] = this.sort_field;
-            }
-            const searchOps = {
-                offset: pager.offset,
-                limit: pager.limit,
-                order_by: orderBy
-            };
-            return this.pcrud.retrieveAll(this.idl_class, searchOps, {fleshSelectors: true});
-        };
-    }
-
-    showEditDialog(standingPenalty: IdlObject): Promise<any> {
-        this.editDialog.mode = 'update';
-        this.editDialog.recordId = standingPenalty['id']();
-        return new Promise((resolve, reject) => {
-            this.editDialog.open({size: this.dialog_size}).subscribe(
-                result => {
-                    this.successString.current()
-                        .then(str => this.toast.success(str));
-                    this.grid.reload();
-                    resolve(result);
-                },
-                error => {
-                    this.updateFailedString.current()
-                        .then(str => this.toast.danger(str));
-                    reject(error);
-                }
-            );
-        });
-    }
-
-    createNew() {
-        this.editDialog.mode = 'create';
-        this.editDialog.recordId = null;
-        this.editDialog.record = null;
-        this.editDialog.open({size: this.dialog_size}).subscribe(
-            ok => {
-                this.createString.current()
-                    .then(str => this.toast.success(str));
-                this.grid.reload();
-            },
-            rejection => {
-                if (!rejection.dismissed) {
-                    this.createErrString.current()
-                        .then(str => this.toast.danger(str));
-                }
-            }
-        );
-    }
-
-    editSelected(fields: IdlObject[]) {
-        // Edit each IDL thing one at a time
-        const editOneThing = (field_object: IdlObject) => {
-            if (!field_object) { return; }
-            this.showEditDialog(field_object).then(
-                () => editOneThing(fields.shift()));
-        };
-        editOneThing(fields.shift());
-    }
-
-    archiveSelected(course: IdlObject[]) {
-        this.courseSvc.disassociateMaterials(course).then(res => {
-            course.forEach(course => {
-                console.log(course);
-                course.is_archived(true);
-            });
-            this.pcrud.update(course).subscribe(
-                val => {
-                    console.debug('archived: ' + val);
-                    this.archiveSuccessString.current()
-                        .then(str => this.toast.success(str));
-                }, err => {
-                    this.archiveFailedString.current()
-                        .then(str => this.toast.danger(str));
-                }, () => {
-                    this.grid.reload();
-                }
-            );
-        });
-    }
-
-    deleteSelected(idl_object: IdlObject[]) {
-        this.courseSvc.disassociateMaterials(idl_object).then(res => {
-            idl_object.forEach(idl_object => {
-                idl_object.isdeleted(true)
-            });
-            this.pcrud.autoApply(idl_object).subscribe(
-                val => {
-                    console.debug('deleted: ' + val);
-                    this.deleteSuccessString.current()
-                        .then(str => this.toast.success(str));
-                },
-                err => {
-                    this.deleteFailedString.current()
-                        .then(str => this.toast.danger(str));
-                },
-                () => this.grid.reload()
-            );
-        });
-    };
-
-    fetchCourseMaterials(course, currentMaterials): Promise<any> {
-        return new Promise((resolve, reject) => {
-            this.pcrud.search('acmcm', {course: course}).subscribe(res => {
-                if (res) this.fleshItemDetails(res.item(), res.relationship());
-            }, err => {
-                reject(err);
-            }, () => resolve(this.courseMaterialDialog.gridDataSource.data));
-        });
-    }
-
-    fleshItemDetails(itemId, relationship): Promise<any> {
-        return new Promise((resolve, reject) => {
-            this.net.request(
-                'open-ils.circ',
-                'open-ils.circ.copy_details.retrieve',
-                this.auth.token(), itemId
-            ).subscribe(res => {
-                if (res) {
-                    let item = res.copy;
-                    item.call_number(res.volume);
-                    item._title = res.mvr.title();
-                    item.circ_lib(this.org.get(item.circ_lib()));
-                    item._relationship = relationship;
-                    this.courseMaterialDialog.gridDataSource.data.push(item);
-                }
-            }, err => {
-                reject(err);
-            }, () => resolve(this.courseMaterialDialog.gridDataSource.data));
-        });
-    }
-
-    openMaterialsDialog(course) {
-        let currentMaterials = []
-        this.courseMaterialDialog.gridDataSource.data = [];
-        this.fetchCourseMaterials(course[0].id(), currentMaterials).then(res => {
-            this.courseMaterialDialog.currentCourse = course[0];
-            this.courseMaterialDialog.materials = currentMaterials;
-            this.courseMaterialDialog.open({size: 'lg'}).subscribe(res => {
-                console.log(res);
-            });
-        });
-    }
-}
-
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-reserves.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course-reserves.module.ts
deleted file mode 100644 (file)
index 6f6eed5..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-import {NgModule} from '@angular/core';
-import {TreeModule} from '@eg/share/tree/tree.module';
-import {AdminCommonModule} from '@eg/staff/admin/common.module';
-import {CourseListComponent} from './course-list.component';
-import {CourseAssociateMaterialComponent} from './course-associate-material.component';
-import {CourseReservesRoutingModule} from './routing.module';
-import {ItemLocationSelectModule} from '@eg/share/item-location-select/item-location-select.module';
-import {CourseService} from './course.service'
-@NgModule({
-  declarations: [
-    CourseListComponent,
-    CourseAssociateMaterialComponent
-  ],
-  imports: [
-    AdminCommonModule,
-    CourseReservesRoutingModule,
-    ItemLocationSelectModule,
-    TreeModule
-  ],
-  exports: [
-  ],
-  providers: [
-    CourseService
-  ]
-})
-
-export class CourseReservesModule {
-}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course.service.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/course.service.ts
deleted file mode 100644 (file)
index c8b60cc..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-import {AuthService} from '@eg/core/auth.service';
-import {EventService} from '@eg/core/event.service';
-import {IdlObject, IdlService} from '@eg/core/idl.service';
-import {NetService} from '@eg/core/net.service';
-import {PcrudService} from '@eg/core/pcrud.service';
-
-export class CourseService {
-
-    constructor(
-        private auth: AuthService,
-        private evt: EventService,
-        private idl: IdlService,
-        private net: NetService,
-        private pcrud: PcrudService
-    ) {}
-
-    disassociateMaterials(courses) {
-        return new Promise((resolve, reject) => {
-            let course_ids = [];
-            let course_library_hash = {};
-            courses.forEach(course => {
-                course_ids.push(course.id());
-                course_library_hash[course.id()] = course.owning_lib();
-            });
-            this.pcrud.search('acmcm', {course: course_ids}).subscribe(material => {
-                material.isdeleted(true);
-                this.resetItemFields(material, course_library_hash[material.course()]);
-                this.pcrud.autoApply(material).subscribe(res => {
-                    console.log(res);
-                }, err => {
-                    reject(err);
-                }, () => {
-                    resolve(material);
-                });
-            }, err => {
-                reject(err)
-            }, () => {
-                resolve(courses);
-            });
-        });
-    }
-
-    resetItemFields(material, course_lib) {
-        this.pcrud.retrieve('acp', material.item(),
-            {flesh: 3, flesh_fields: {acp: ['call_number']}}).subscribe(copy => {
-            if (material.original_status()) {
-                copy.status(material.original_status());
-            }
-            if (copy.circ_modifier() != material.original_circ_modifier()) {
-                copy.circ_modifier(material.original_circ_modifier());
-            }
-            if (material.original_location()) {
-                copy.location(material.original_location());
-            }
-            if (material.original_callnumber()) {
-                this.pcrud.retrieve('acn', material.original_callnumber()).subscribe(cn => {
-                    this.updateItem(copy, course_lib, cn.label(), true);
-                });
-            } else {
-                this.updateItem(copy, course_lib, copy.call_number().label(), false);
-            }
-        });
-    }
-
-    updateItem(item: IdlObject, course_lib, call_number, updatingVolume) {
-        return new Promise((resolve, reject) => {
-            this.pcrud.update(item).subscribe(item_id => {
-                if (updatingVolume) {
-                    let cn = item.call_number();
-                    return this.net.request(
-                        'open-ils.cat', 'open-ils.cat.call_number.find_or_create',
-                        this.auth.token(), call_number, cn.record(),
-                        course_lib, cn.prefix(), cn.suffix(),
-                        cn.label_class()
-                    ).subscribe(res => {
-                        let event = this.evt.parse(res);
-                        if (event) return;
-                        return this.net.request(
-                            'open-ils.cat', 'open-ils.cat.transfer_copies_to_volume',
-                            this.auth.token(), res.acn_id, [item.id()]
-                        ).subscribe(transfered_res => {
-                            console.debug("Copy transferred to volume with code " + transfered_res);
-                        }, err => {
-                            reject(err);
-                        }, () => {
-                            resolve(item);
-                        });
-                    }, err => {
-                        reject(err);
-                    }, () => {
-                        resolve(item);
-                    });
-                } else {
-                    return this.pcrud.update(item);
-                }
-            });
-        });
-    }
-
-}
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/course-reserves/routing.module.ts
deleted file mode 100644 (file)
index 84e74ff..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-import {NgModule} from '@angular/core';
-import {RouterModule, Routes} from '@angular/router';
-import {CourseListComponent} from './course-list.component';
-
-const routes: Routes = [{
-    path: '',
-    component: CourseListComponent
-}];
-
-@NgModule({
-  imports: [RouterModule.forChild(routes)],
-  exports: [RouterModule]
-})
-
-export class CourseReservesRoutingModule {}
\ No newline at end of file
index fe4d354..194a979 100644 (file)
@@ -23,9 +23,6 @@ const routes: Routes = [{
     path: 'permission/grp_tree',
     component: PermGroupTreeComponent
 }, {
-    path: 'asset/course_list',
-    loadChildren: '@eg/staff/admin/server/course-reserves/course-reserves.module#CourseReservesModule'
-}, {
     path: 'actor/org_unit',
     loadChildren: () =>
       import('./org-unit.module').then(m => m.OrgUnitModule)