<li [ngbNavItem]="'courseTerms'">
<a ngbNavLink i18n>Course terms</a>
<ng-template ngbNavContent>
- <eg-admin-page idlClass="acmtcm" readonlyFields="id"
- [defaultNewRecord]="defaultNewAcmtcm"
- hideGridFields="id" [dataSource]="termsDataSource">
- </eg-admin-page>
+ <eg-course-term-map-grid [courseId]="courseId"></eg-course-term-map-grid>
</ng-template>
</li>
</ul>
import {Component, ViewChild, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {PcrudService} from '@eg/core/pcrud.service';
-import {IdlObject, IdlService} from '@eg/core/idl.service';
-import {GridDataSource} from '@eg/share/grid/grid';
+import {IdlObject} from '@eg/core/idl.service';
import {StringComponent} from '@eg/share/string/string.component';
import {ToastService} from '@eg/share/toast/toast.service';
import {CourseService} from '@eg/staff/share/course.service';
import {CourseAssociateUsersComponent} from './course-associate-users.component';
import {CourseAssociateMaterialComponent} from './course-associate-material.component';
-import {Pager} from '@eg/share/util/pager';
@Component({
selector: 'eg-course-page',
@ViewChild('archiveSuccessString', { static: true })
archiveSuccessString: StringComponent;
- // Course Tab
- termsDataSource: GridDataSource = new GridDataSource();
- defaultNewAcmtcm: IdlObject;
-
constructor(
private course: CourseService,
- private idl: IdlService,
private pcrud: PcrudService,
private route: ActivatedRoute,
private toast: ToastService
this.course.getCourses([this.courseId]).then(course => {
this.currentCourse = course[0];
});
-
- this.defaultNewAcmtcm = this.idl.create('acmtcm');
- this.defaultNewAcmtcm.course(this.courseId);
-
- this.termsDataSource.getRows = (pager: Pager, sort: any[]) => {
- const orderBy: any = {};
- if (sort.length) {
- orderBy.acmtcm = sort[0].name + ' ' + sort[0].dir;
- }
- const searchOps = {
- offset: pager.offset,
- limit: pager.limit,
- order_by: orderBy
- };
-
- return this.pcrud.search('acmtcm', {course: this.courseId},
- searchOps, {fleshSelectors: true});
- };
}
// Edit Tab
import {MarcSimplifiedEditorModule} from '@eg/staff/share/marc-edit/simplified-editor/simplified-editor.module';
import {PatronModule} from '@eg/staff/share/patron/patron.module';
import {CourseTermMapComponent} from './course-term-map.component';
+import {CourseTermMapGridComponent} from './course-term-map-grid.component';
@NgModule({
declarations: [
CoursePageComponent,
CourseAssociateMaterialComponent,
CourseAssociateUsersComponent,
- CourseTermMapComponent
+ CourseTermMapComponent,
+ CourseTermMapGridComponent
],
imports: [
StaffCommonModule,
--- /dev/null
+<eg-grid #grid idlClass="acmtcm" [dataSource]="gridDataSource" [filterable]="true">
+ <eg-grid-toolbar-button
+ label="Attach course to a term"
+ i18n-label (onClick)="createNew()"></eg-grid-toolbar-button>
+ <eg-grid-toolbar-action label="Detach course(s) from term(s)" i18n-label
+ [action]="deleteSelected"></eg-grid-toolbar-action>
+</eg-grid>
+
+<eg-fm-record-editor #editDialog idlClass="acmtcm"
+ [fieldOptions]="{term:{customTemplate:{template:termTemplate}}}"
+ fieldOrder="course,term"
+ hiddenFieldsList="id"
+ [defaultNewRecord]="defaultNewAcmtcm"
+ [readonlyFieldsList]="readonlyFields">
+</eg-fm-record-editor>
+
+<ng-template #termTemplate let-field="field" let-record="record">
+ <eg-combobox [required]="true"
+ [asyncSupportsEmptyTermClick]="true"
+ [asyncDataSource]="termEntryGenerator(record['course']())"
+ [selectedId]="record['term']()"
+ (onChange)="($event) ? record['term']($event.id) : ''">
+ </eg-combobox>
+</ng-template>
--- /dev/null
+import {Component, Input, OnInit, ViewChild} from '@angular/core';
+import {Observable} from 'rxjs';
+import {map, switchMap} from 'rxjs/operators';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {Pager} from '@eg/share/util/pager';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.service';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+
+@Component({
+ selector: 'eg-course-term-map-grid',
+ templateUrl: './course-term-map-grid.component.html',
+})
+export class CourseTermMapGridComponent implements OnInit {
+ @Input() courseId: number;
+ @ViewChild('grid') private grid: GridComponent;
+ @ViewChild('editDialog') private editDialog: FmRecordEditorComponent;
+
+ readonlyFields: string;
+
+ defaultNewAcmtcm: IdlObject;
+ gridDataSource: GridDataSource;
+ createNew: () => void;
+ deleteSelected: (rows: IdlObject[]) => void;
+ termEntryGenerator: (course: number) => (query: string) => Observable<ComboboxEntry>;
+ termEntries: (query: string) => Observable<ComboboxEntry>;
+
+ constructor(
+ private idl: IdlService,
+ private org: OrgService,
+ private pcrud: PcrudService,
+ ) {
+ this.gridDataSource = new GridDataSource();
+ this.defaultNewAcmtcm = this.idl.create('acmtcm');
+ }
+
+ ngOnInit() {
+
+ if (this.courseId) {
+ this.defaultNewAcmtcm.course(this.courseId);
+ this.readonlyFields = 'course';
+ }
+
+ this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
+ const orderBy: any = {};
+
+ const searchOps = {
+ offset: pager.offset,
+ limit: pager.limit,
+ order_by: orderBy
+ };
+
+ const criteria = this.courseId ? {course: this.courseId} : {};
+
+ return this.pcrud.search('acmtcm',
+ criteria, searchOps, {fleshSelectors: true});
+ };
+
+ // Produce a bespoke callback for the combobox search, which
+ // limits the results to course terms that make sense for the
+ // selected course. This prevents users from associating a
+ // course at their library to a term from a completely different
+ // academic calendar.
+ this.termEntryGenerator = (courseId: number) => {
+ return (query: string) => {
+ return this.pcrud.retrieve('acmc', courseId).pipe(switchMap(fullCourseObject => {
+ return this.pcrud.search(
+ 'acmt', {
+ name: {'ilike': `%${query}`},
+ owning_lib: this.org.ancestors(fullCourseObject.owning_lib(), true)
+ },
+ {order_by: {'acmt': 'name'}}
+ );
+ }), map(courseTerm => {
+ return {id: courseTerm.id(), label: courseTerm.name()};
+ }));
+ };
+ };
+
+ this.createNew = () => {
+ this.editDialog.mode = 'create';
+ this.editDialog.open({size: 'lg'})
+ .subscribe(() => this.grid.reload());
+ };
+
+ this.deleteSelected = (termMaps: IdlObject[]) => {
+ termMaps.forEach(termMap => termMap.isdeleted(true));
+ this.pcrud.autoApply(termMaps).subscribe(
+ val => console.debug('deleted: ' + val),
+ err => {},
+ () => this.grid.reload()
+ );
+ };
+ }
+}
import {Component} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
/**
* Very basic page for editing course/term map
</a>
</div>
</div>
- <eg-admin-page persistKeyPfx="local" idlClass="acmtcm"
- [disableOrgFilter]="true"></eg-admin-page>
+ <eg-course-term-map-grid [courseId]="courseId"></eg-course-term-map-grid>
`
})
export class CourseTermMapComponent {
+ public courseId: number;
+
+ constructor(private route: ActivatedRoute) {
+ const filters = this.route.snapshot.queryParamMap.get('gridFilters');
+ this.courseId = JSON.parse(filters)['course'] || 1;
+ }
+
}