Newer
Older
import { Component, Input, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material';
import { Store, select } from '@ngrx/store';
import {
AddCourse,
AddSaveForLater,
MoveCourseBetweenTerms,
import { GlobalState } from '@app/core/state';
import * as selectors from '@app/degree-planner/store/selectors';
import { DegreePlannerApiService } from '@app/degree-planner/services/api.service';
import { ConfirmDialogComponent } from '@app/shared/dialogs/confirm-dialog/confirm-dialog.component';
import { CourseDetailsDialogComponent } from '@app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component';
import { distinctUntilChanged, filter } from 'rxjs/operators';
import { TermCode } from '@app/core/models/termcode';
import { PlannedTerm, PlannedTermEra } from '@app/core/models/planned-term';
const isntUndefined = <T>(thing: T | undefined): thing is T => {
return thing !== undefined;
};
selector: 'cse-course-item',
templateUrl: './course-item.component.html',
styleUrls: ['./course-item.component.scss'],
export class CourseItemComponent implements OnInit {
@Input() isCurrentTerm: boolean;
@Input() isPastTerm: boolean;
@Input() disabled: boolean;
activeTerm: any;
public status:
| 'InProgress'
| 'Waitlisted'
| 'Incomplete'
| 'NotOfferedInTerm'
| 'NoLongerOffered'
| 'Normal';
public visibleTermCodes$: Observable<string[]>;
public droppableTermCodes$: Observable<string[]>;
public term$: Observable<PlannedTerm>;
public plannedCourses: Course[];
public toActiveTerm: boolean;
constructor(
private api: DegreePlannerApiService,
public dialog: MatDialog,
private store: Store<GlobalState>,
) {}
ngOnInit() {
const isActive = this.era === 'active';
const isPast = this.era === 'past';
const isNotOffered = this.course.studentEnrollmentStatus === 'NOTOFFERED';
const isNoLongerOffered =
this.course.studentEnrollmentStatus === 'DOESNOTEXIST';
this.isStruckthrough = (isPast || isActive) && isNoLongerOffered;
const isIncomplete = isPast && this.course.grade === null;
const isWaitlisted = this.course.studentEnrollmentStatus === 'Waitlisted';
const isInProgress =
isActive && this.course.studentEnrollmentStatus === 'Enrolled';
if (isIncomplete) {
this.status = 'Incomplete';
} else if (isWaitlisted) {
this.status = 'Waitlisted';
} else if (isInProgress) {
this.status = 'InProgress';
} else if (isNotOffered) {
this.status = 'NotOfferedInTerm';
this.droppableTermCodes$ = this.store.pipe(
select(selectors.selectAllVisibleYears),
utils.yearsToDroppableTermCodes(),
distinctUntilChanged(utils.compareStringArrays),
);
this.store.dispatch(
new AddSaveForLater({
courseId: course.courseId,
subjectCode: course.subjectCode,
title: course.title,
catalogNumber: course.catalogNumber,
newIndex: 0,
/**
*
* Handle moving a course to different terms based on course type
*
*/
onMove(termCode: string) {
const toTermCode = new TermCode(termCode);
this.term$ = this.store.pipe(
select(selectors.selectVisibleTerm, { termCode: toTermCode }),
filter(isntUndefined),
distinctUntilChanged(),
);
this.term$.subscribe(term => {
this.plannedCourses = term.plannedCourses;
this.toActiveTerm = term.era === 'active';
});
const isCourseInPlannedCourses = this.plannedCourses.some(
course => course.courseId === this.course.courseId,
);
const courseNotOfferedInTerm = this.plannedCourses.some(
course =>
course.studentEnrollmentStatus === 'DOESNOTEXIST' ||
course.studentEnrollmentStatus === 'NOTOFFERED',
);
if (isCourseInPlannedCourses) {
this.dialog
.open(ConfirmDialogComponent, {
data: {
title: "Can't add course",
confirmText: 'OK',
dialogClass: 'alertDialog',
text: `This course already exists in selected term!`,
},
})
.afterClosed();
return;
}
if (this.toActiveTerm && courseNotOfferedInTerm) {
const notOfferedCourse =
this.course.subject + ' ' + this.course.catalogNumber;
this.dialog
.open(ConfirmDialogComponent, {
data: {
title: "Can't add course to term",
confirmText: 'OK',
dialogClass: 'alertDialog',
text: `${notOfferedCourse} is not offered in ${
toTermCode.description
}`,
},
})
.afterClosed();
return;
}
case 'course': {
const id = this.course.id as number;
new MoveCourseBetweenTerms({
to: toTermCode,
from,
id,
newIndex: 0,
this.store.dispatch(new RemoveSaveForLater({ subjectCode, courseId }));
break;
}
}
/**
*
* Handle saving a course for later (This is not possible if a course is already saved)
*
*/
onSaveForLater() {
const {
courseId,
subjectCode,
title,
catalogNumber,
termCode,
} = this.course;
// Dispatch a save for later event
this.store.dispatch(
new AddSaveForLater({
courseId: courseId,
subjectCode: subjectCode,
title: title,
catalogNumber: catalogNumber,
newIndex: 0,
}),
);
// If course is in a term, we need to remove it
if (this.type === 'course') {
this.store.dispatch(
new RemoveCourse({
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
recordId: this.course.id as number,
}),
);
}
}
/**
*
* Handle removing a course (This is not possible for type 'search')
*
*/
onRemove() {
const dialogOptions = {
title: 'Remove Course?',
text: '',
confirmText: 'Remove Course',
confirmColor: 'accent',
};
switch (this.type) {
case 'saved':
dialogOptions.text = `This will remove "${
this.course.title
}" from your saved courses.`;
break;
default:
dialogOptions.text = `This will remove "${
this.course.title
}" from your degree plan and your cart.`;
}
this.dialog
.open(ConfirmDialogComponent, { data: dialogOptions })
.afterClosed()
.subscribe((result: { confirmed: boolean }) => {
// If the user confirmed the removal, remove course
console.log(this.type);
console.log({
type: this.type,
fromTermCode: this.course.termCode,
recordId: this.course.id,
});
switch (this.type) {
case 'course':
this.store.dispatch(
new RemoveCourse({
recordId: this.course.id as number,
}),
);
break;
case 'saved':
const { subjectCode, courseId } = this.course;
this.store.dispatch(
new RemoveSaveForLater({ subjectCode, courseId }),
);
break;
}
}
});
}
this.store.dispatch(
new AddCourse({
courseId: this.course.courseId,
termCode: toTermCode,
subjectCode: this.course.subjectCode,
title: this.course.title,
catalogNumber: this.course.catalogNumber,
}),
);
this.api
.getCourseDetails(course.subjectCode, course.courseId)
.subscribe(courseDetails => {
const dialogRef = this.dialog.open(CourseDetailsDialogComponent, {
width: '80%',
data: { courseDetails: courseDetails, courseType: this.type },