<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 #deleteFailedString i18n-text text="Deletion of {{table_name}} failed or was not allowed"></eg-string>
+<eg-string #deleteSucailedString i18n-text text="Deletion of {{table_name}} was successful"></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>
import {Component, Input, ViewChild, OnInit} from '@angular/core';
+import { Router, ActivatedRoute } from '@angular/router';
import {IdlObject} from '@eg/core/idl.service';
import {PcrudService} from '@eg/core/pcrud.service';
import {AuthService} from '@eg/core/auth.service';
private net: NetService,
private org: OrgService,
private pcrud: PcrudService,
- private toast: ToastService,
+ private route: ActivatedRoute,
+ private router: Router,
+ private toast: ToastService
){}
ngOnInit() {
this.getSource();
+ this.grid.onRowActivate.subscribe((course:IdlObject) => {
+ let idToEdit = course.id();
+ this.navigateToCoursePage(idToEdit);
+ })
}
/**
};
}
+ navigateToCoursePage(id: any) {
+ this.router.navigate(["/staff/admin/local/asset/course_list/" + id]);
+ }
+
showEditDialog(standingPenalty: IdlObject): Promise<any> {
this.editDialog.mode = 'update';
this.editDialog.recordId = standingPenalty['id']();
--- /dev/null
+<eg-staff-banner bannerText=" {{currentCourse.course_number()}}: {{currentCourse.name()}}"
+ i18n-bannerText class="mb-3" *ngIf="currentCourse">
+</eg-staff-banner>
+<ngb-tabset #surveyTabs [activeId]="surveyTab" class="mb-3">
+ <ngb-tab title="Edit Course" i18n-title id="edit">
+ <ng-template ngbTabContent>
+ <div class="col-lg-6 offset-lg-3 mt-3">
+ <div style="text-align: center;">
+ <button class="p-2 mb-3 btn btn-danger btn-lg"
+ (click)="archiveCourse()">
+ Archive Course
+ </button>
+ </div>
+ <eg-fm-record-editor displayMode="inline"
+ hiddenFieldsList="id,is_archived"
+ idlClass="acmc"
+ [preloadLinkedValues]="true"
+ [record]="currentCourse">
+ </eg-fm-record-editor>
+ </div>
+ </ng-template>
+ </ngb-tab>
+ <ngb-tab title="Course Materials" i18n-title id="courseMaterials">
+ <ng-template ngbTabContent>
+ <div class="row mt-3">
+ <!-- Input Sidebar -->
+ <div class="col-lg-4 mt-3">
+ <div class="row mt-3">
+ <div class="col-lg-12 d-flex">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <span class="input-group-text" i18n>Barcode</span>
+ </div>
+ <input type="text" class="flex-grow-1" [(ngModel)]="barcodeInput"
+ (click)="$event.target.select()"
+ (keyup.enter)="associateItem(barcodeInput, relationshipInput)" />
+ </div>
+ </div>
+ </div>
+ <div class="row mt-3">
+ <div class="col-lg-12 d-flex">
+ <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"
+ class="flex-grow-1" />
+ </div>
+ </div>
+ </div>
+ <div class="row mt-3">
+ <div class="col-lg-12 text-right">
+ <button class="btn btn-primary" (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-lg-12 d-flex">
+ <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" class="flex-grow-1" />
+ <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>
+ <div class="row mt-3">
+ <div class="col-lg-12 d-flex">
+ <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" class="flex-grow-1"
+ (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-lg-12 d-flex">
+ <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" class="flex-grow-1"
+ (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>
+ <div class="row mt-3">
+ <div class="col-lg-12 d-flex">
+ <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" class="flex-grow-1"
+ [(ngModel)]="tempLocation" (valueChange)="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>
+ <!-- End Input Sidebar -->
+ <div class="col-lg-8 mt-3">
+ <eg-grid #materialsGrid [dataSource]="materialsDataSource">
+ <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>
+ </ngb-tab>
+</ngb-tabset>
+
+<eg-string #archiveFailedString i18n-text text="Archival of Course failed or was not allowed"></eg-string>
+<eg-string #archiveSuccessString i18n-text text="Archival of Course succeeded"></eg-string>
+<eg-string #materialDeleteFailedString i18n-text text="Disassociation of Course Material failed or was not allowed"></eg-string>
+<eg-string #materialDeleteSuccessString i18n-text text="Disassociation of Course Material succeeded"></eg-string>
+<eg-string #materialAddSuccessString i18n-text text="Association of Course Material succeeded"></eg-string>
+<eg-string #materialAddFailedString i18n-text text="Association of Course Material failed or was not allowed"></eg-string>
+<eg-string #MaterialAddDifferentLibraryString i18n-text text="Material exists at a different library"></eg-string>
\ No newline at end of file
--- /dev/null
+import {Component, Input, ViewChild, OnInit, TemplateRef} from '@angular/core';
+import {Router, ActivatedRoute} from '@angular/router';
+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 {StaffBannerComponent} from '@eg/staff/share/staff-banner.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {CourseService} from '@eg/staff/share/course.service';
+
+@Component({
+ selector: 'eg-course-page',
+ templateUrl: './course-page.component.html'
+})
+
+export class CoursePageComponent implements OnInit {
+
+ currentCourse: IdlObject;
+ courseId: any;
+
+ // Edit Tab
+ @ViewChild('archiveFailedString', { static: true })
+ archiveFailedString: StringComponent;
+ @ViewChild('archiveSuccessString', { static: true })
+ archiveSuccessString: StringComponent;
+
+ // Materials Tab
+ materials: any[] = [];
+ @ViewChild('materialsGrid', {static: true}) materialsGrid: GridComponent;
+ @ViewChild('materialDeleteFailedString', { static: true })
+ materialDeleteFailedString: StringComponent;
+ @ViewChild('materialDeleteSuccessString', { static: true })
+ materialDeleteSuccessString: StringComponent;
+ @ViewChild('materialAddSuccessString', { static: true })
+ materialAddSuccessString: StringComponent;
+ @ViewChild('materialAddFailedString', { static: true })
+ materialAddFailedString: StringComponent;
+ @ViewChild('materialAddDifferentLibraryString', { static: true })
+ materialAddDifferentLibraryString: StringComponent;
+ materialsDataSource: GridDataSource;
+ @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;
+
+ constructor(
+ private auth: AuthService,
+ private course: CourseService,
+ private event: EventService,
+ private idl: IdlService,
+ private org: OrgService,
+ private pcrud: PcrudService,
+ private route: ActivatedRoute,
+ private toast: ToastService
+ ) {
+ this.materialsDataSource = new GridDataSource();
+ }
+
+ ngOnInit() {
+ this.courseId = parseInt(this.route.snapshot.paramMap.get('id'));
+ this.course.getCourses([this.courseId]).then(course => {
+ this.currentCourse = course[0];
+ });
+ this.materialsDataSource.getRows = (pager: Pager, sort: any[]) => {
+ return this.loadMaterialsGrid(pager);
+ }
+ }
+
+ // Edit Tab
+ archiveCourse() {
+ this.course.disassociateMaterials(this.currentCourse).then(res => {
+ this.currentCourse.is_archived(true);
+ this.pcrud.update(this.currentCourse).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));
+ });
+ });
+ }
+
+ // Materials Tab
+ loadMaterialsGrid(pager: Pager): Observable<any> {
+ return new Observable<any>(observer => {
+ this.course.getMaterials(this.courseId).then(materials => {
+ materials.forEach(material => {
+ this.course.fleshMaterial(material.item(), material.relationship()).then(fleshed_material => {
+ this.materialsDataSource.data.push(fleshed_material);
+ });
+ });
+ });
+ observer.complete();
+ });
+ }
+
+ associateItem(barcode, relationship) {
+ if (barcode) {
+ let args = {
+ barcode: barcode,
+ relationship: relationship,
+ isModifyingCallNumber: this.isModifyingCallNumber,
+ isModifyingCircMod: this.isModifyingCircMod,
+ isModifyingLocation: this.isModifyingLocation,
+ isModifyingStatus: this.isModifyingStatus,
+ tempCircMod: this.tempCircMod,
+ tempLocation: this.tempLocation,
+ tempStatus: this.tempStatus,
+ currentCourse: this.currentCourse
+ }
+ this.barcodeInput = null;
+
+ this.pcrud.search('acp', {barcode: args.barcode}, {
+ flesh: 3, flesh_fields: {acp: ['call_number']}
+ }).subscribe(item => {
+ let associatedMaterial = this.course.associateMaterials(item, args);
+ associatedMaterial.material.then(res => {
+ item = associatedMaterial.item;
+ let new_cn = item.call_number().label();
+ if (this.tempCallNumber) new_cn = this.tempCallNumber;
+ this.course.updateItem(item, this.currentCourse.owning_lib(),
+ new_cn, args.isModifyingCallNumber
+ ).then(resp => {
+ this.course.fleshMaterial(item.id(), args.relationship).then(fleshed_material => {
+ this.materialsDataSource.data.push(fleshed_material);
+ });
+ if (item.circ_lib() != this.currentCourse.owning_lib()) {
+ this.materialAddDifferentLibraryString.current()
+ .then(str => this.toast.warning(str));
+ } else {
+ this.materialAddSuccessString.current()
+ .then(str => this.toast.success(str));
+ }
+ });
+ }, err => {
+ this.materialAddFailedString.current()
+ .then(str => this.toast.danger(str));
+ });
+ });
+ }
+ }
+
+ deleteSelected(items) {
+ let item_ids = [];
+ items.forEach(item => {
+ this.materialsDataSource.data.splice(this.materialsDataSource.data.indexOf(item, 0), 1);
+ item_ids.push(item.id())
+ });
+ this.pcrud.search('acmcm', {course: this.courseId, item: item_ids}).subscribe(material => {
+ material.isdeleted(true);
+ this.pcrud.autoApply(material).subscribe(
+ val => {
+ this.course.resetItemFields(material, this.currentCourse.owning_lib());
+ console.debug('deleted: ' + val);
+ this.materialDeleteSuccessString.current().then(str => this.toast.success(str));
+ },
+ err => {
+ this.materialDeleteFailedString.current()
+ .then(str => this.toast.danger(str));
+ }
+ );
+ });
+ }
+}
\ No newline at end of file
import {StaffCommonModule} from '@eg/staff/common.module';
import {AdminCommonModule} from '@eg/staff/admin/common.module';
import {CourseListComponent} from './course-list.component';
+import {CoursePageComponent} from './course-page.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';
@NgModule({
declarations: [
CourseListComponent,
+ CoursePageComponent,
CourseAssociateMaterialComponent
],
imports: [
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {CourseListComponent} from './course-list.component';
+import {CoursePageComponent} from './course-page.component';
const routes: Routes = [{
+ path: ':id',
+ component: CoursePageComponent
+}, {
path: '',
component: CourseListComponent
}];
}
}
+ getMaterials(course_ids?: Number[]): Promise<IdlObject[]> {
+ if (!course_ids) {
+ return this.pcrud.retrieveAll('acmcm',
+ {}, {atomic: true}).toPromise();
+ } else {
+ return this.pcrud.search('acmcm', {course: course_ids},
+ {}, {atomic: true}).toPromise();
+ }
+ }
+
+ fleshMaterial(itemId, relationship?): Promise<any> {
+ return new Promise((resolve, reject) => {
+ let item = this.idl.create('acp');
+ this.net.request(
+ 'open-ils.circ',
+ 'open-ils.circ.copy_details.retrieve',
+ this.auth.token(), itemId
+ ).subscribe(res => {
+ if (res && res.copy) {
+ item = res.copy;
+ item.call_number(res.volume);
+ item._title = res.mvr.title();
+ item.circ_lib(this.org.get(item.circ_lib()));
+ if (relationship) item._relationship = relationship;
+ }
+ }, err => {
+ reject(err);
+ }, () => resolve(item));
+ });
+ }
+
getCoursesFromMaterial(copy_id): Promise<any> {
let id_list = [];
return new Promise((resolve, reject) => {
id_list.push(materials.course());
}
}, err => {
- console.log(err);
+ console.debug(err);
reject(err);
}, () => {
if (id_list.length) {
// Creating a new acmcm Entry
associateMaterials(item, args) {
- console.log("entering associateMaterials")
let material = this.idl.create('acmcm');
material.item(item.id());
material.course(args.currentCourse.id());
material.isdeleted(true);
this.resetItemFields(material, course_library_hash[material.course()]);
this.pcrud.autoApply(material).subscribe(res => {
- console.log(res);
}, err => {
reject(err);
}, () => {
resolve(item);
});
} else {
- return this.pcrud.update(item);
+ this.pcrud.update(item).subscribe(rse => {
+ resolve(item);
+ }, err => {
+ reject(item);
+ });
}
});
});