From 9f73733559d3bdade213ef6c43b60541fd038293 Mon Sep 17 00:00:00 2001
From: Paulina Nogal <pnogal@wisc.edu>
Date: Wed, 19 Dec 2018 16:13:24 +0000
Subject: [PATCH] Create three dot menu for course items and add its
 functionality

---
 .../degree-planner.component.html             |   3 +-
 .../degree-planner.component.ts               |   7 ++
 .../degree-planner/degree-planner.module.ts   |   6 +-
 ...emove-course-confirm-dialog.component.html |  14 +++
 ...emove-course-confirm-dialog.component.scss |   6 ++
 ...ve-course-confirm-dialog.component.spec.ts |  25 +++++
 .../remove-course-confirm-dialog.component.ts |  50 +++++++++
 .../favorites-container.component.html        |   4 +
 .../favorites-container.component.ts          |   8 +-
 .../course-item/course-item.component.html    |  62 +++++++----
 .../course-item/course-item.component.scss    |  35 +++---
 .../course-item/course-item.component.ts      | 101 +++++++++++++++++-
 .../sidenav-menu-item.component.html          |   2 +-
 .../sidenav-menu-item.component.ts            |   2 +
 .../term-container.component.html             |  81 +++++++-------
 .../term-container.component.ts               |   1 +
 src/app/shared/academic-year-range.pipe.ts    |  23 ++++
 src/app/shared/shared.module.ts               |   5 +-
 src/assets/sass/general.scss                  |  27 +++++
 19 files changed, 376 insertions(+), 86 deletions(-)
 create mode 100644 src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.html
 create mode 100644 src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.scss
 create mode 100644 src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.spec.ts
 create mode 100644 src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.ts
 create mode 100644 src/app/shared/academic-year-range.pipe.ts

diff --git a/src/app/degree-planner/degree-planner.component.html b/src/app/degree-planner/degree-planner.component.html
index 6e05000..5604583 100644
--- a/src/app/degree-planner/degree-planner.component.html
+++ b/src/app/degree-planner/degree-planner.component.html
@@ -1,7 +1,7 @@
 <mat-sidenav-container id="plans-container">
 	<!-- Menu side nav -->
 	<mat-sidenav #rightMenu position="end" [mode]="mobileView.matches ? 'over' : 'side'" [opened]="mobileView.matches ? false : true">
-		<cse-sidenav-menu-item [favoriteDropZone]="getTermDropZone()" [subjectsMap]="subjectsMap"></cse-sidenav-menu-item>
+		<cse-sidenav-menu-item [termsByAcademicYear]="termsByAcademicYear" [favoriteCourses]="favoriteCourses" [favoriteDropZone]="getTermDropZone()" [subjectsMap]="subjectsMap"></cse-sidenav-menu-item>
 	</mat-sidenav>
 
 	<mat-sidenav-content>
@@ -33,6 +33,7 @@
 							*ngFor="let term of termsByAcademicYear[year.key].terms | keyvalue" 
 							[term]="termsByAcademicYear[year.key].terms[term.key]" 
 							[courses]="termsByAcademicYear[year.key].terms[term.key]['courses']" 
+							[favoriteCourses]="favoriteCourses"
 							[termCodes]="getTermDropZone()"
 							[termsByAcademicYear]="termsByAcademicYear"
 							[subjectsMap]="subjectsMap"
diff --git a/src/app/degree-planner/degree-planner.component.ts b/src/app/degree-planner/degree-planner.component.ts
index 6f6d25e..c6f84ba 100644
--- a/src/app/degree-planner/degree-planner.component.ts
+++ b/src/app/degree-planner/degree-planner.component.ts
@@ -6,6 +6,7 @@ import { Term } from '../core/models/term';
 import { log } from 'util';
 import { Note } from '../core/models/note';
 import { MediaMatcher } from '@angular/cdk/layout';
+import { FavoriteCourse } from '../core/models/favorite-course';
 
 @Component({
 	selector: 'cse-degree-planner',
@@ -23,6 +24,7 @@ export class DegreePlannerComponent {
 	notes: Note[];
 	firstCurrentTerm: null|string;
 	mobileView: MediaQueryList;
+	favoriteCourses: FavoriteCourse[];
 
 
 	constructor(private dataService: DataService, public mediaMatcher: MediaMatcher) {
@@ -76,6 +78,11 @@ export class DegreePlannerComponent {
 
 		this.dataService.getTerms()
 		.subscribe(terms => {});
+
+		this.dataService.getFavoriteCourses()
+		.subscribe(favoriteCourses => {
+			this.favoriteCourses = favoriteCourses;
+		});
 	}
 
 	// Create a new year object to be used in this.termsByAcademicYear
diff --git a/src/app/degree-planner/degree-planner.module.ts b/src/app/degree-planner/degree-planner.module.ts
index 327334c..e3be2d0 100644
--- a/src/app/degree-planner/degree-planner.module.ts
+++ b/src/app/degree-planner/degree-planner.module.ts
@@ -10,6 +10,7 @@ import { CourseItemComponent } from './shared/course-item/course-item.component'
 import { CourseDetailsDialogComponent } from './dialogs/course-details-dialog/course-details-dialog.component';
 import { NotesDialogComponent } from './dialogs/notes-dialog/notes-dialog.component';
 import { DragDropModule } from '@angular/cdk/drag-drop';
+import { RemoveCourseConfirmDialogComponent } from './dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component';
 
 @NgModule({
 	imports: [
@@ -27,8 +28,9 @@ import { DragDropModule } from '@angular/cdk/drag-drop';
 		SidenavMenuItemComponent,
 		FavoritesContainerComponent,
 		CourseDetailsDialogComponent,
-		NotesDialogComponent
+		NotesDialogComponent,
+		RemoveCourseConfirmDialogComponent
 	],
-	entryComponents: [ CourseDetailsDialogComponent, NotesDialogComponent ]
+	entryComponents: [ CourseDetailsDialogComponent, NotesDialogComponent, RemoveCourseConfirmDialogComponent ]
 })
 export class DegreePlannerModule { }
diff --git a/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.html b/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.html
new file mode 100644
index 0000000..41ddaf1
--- /dev/null
+++ b/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.html
@@ -0,0 +1,14 @@
+<mat-toolbar class="dialog-toolbar">
+    <h1 class="dialog-toolbar-title">Are you sure?</h1>
+    <button mat-button mat-dialog-close class="close-btn" aria-label="Close note dialog"><i class="material-icons">clear</i></button>
+</mat-toolbar>
+<mat-dialog-content id="confirmation-dialog" class="mat-typography dialog-with-toolbar">
+	<mat-dialog-content>
+		<p class="dialog-text" *ngIf="savedForLater">This will remove {{ course.title }} from Saved For Later and My Courses.</p>
+		<p class="dialog-text" *ngIf="!savedForLater">This will remove {{ course.title }} from your plan and from your cart.</p>
+	</mat-dialog-content>
+	<mat-dialog-actions align="end">
+        <button mat-button class="mat-button btn-link" mat-dialog-close aria-label="Close dialog">Cancel</button>
+		<button mat-button class="btn-link" (click)="removeCourse()" mat-dialog-close aria-label="Delete course from selected term">Remove</button>
+	</mat-dialog-actions>
+</mat-dialog-content>
\ No newline at end of file
diff --git a/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.scss b/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.scss
new file mode 100644
index 0000000..0b55a82
--- /dev/null
+++ b/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.scss
@@ -0,0 +1,6 @@
+
+    .dialog-text {
+        color: #7e7e7e;
+        font-size: 16px;
+        margin-top: 1.2em;
+    }
\ No newline at end of file
diff --git a/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.spec.ts b/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.spec.ts
new file mode 100644
index 0000000..870bb46
--- /dev/null
+++ b/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RemoveCourseConfirmDialogComponent } from './remove-course-confirm-dialog.component';
+
+describe('RemoveCourseConfirmDialogComponent', () => {
+	let component: RemoveCourseConfirmDialogComponent;
+	let fixture: ComponentFixture<RemoveCourseConfirmDialogComponent>;
+
+	beforeEach(async(() => {
+		TestBed.configureTestingModule({
+			declarations: [ RemoveCourseConfirmDialogComponent ]
+	})
+	.compileComponents();
+	}));
+
+	beforeEach(() => {
+		fixture = TestBed.createComponent(RemoveCourseConfirmDialogComponent);
+		component = fixture.componentInstance;
+		fixture.detectChanges();
+	});
+
+	it('should create', () => {
+		expect(component).toBeTruthy();
+	});
+});
diff --git a/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.ts b/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.ts
new file mode 100644
index 0000000..4abb262
--- /dev/null
+++ b/src/app/degree-planner/dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component.ts
@@ -0,0 +1,50 @@
+import { Component, OnInit, Input, Inject } from '@angular/core';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
+import { DataService } from '../../../core/data.service';
+import { Course } from '../../../core/models/course';
+import { FavoriteCourse } from '../../../core/models/favorite-course';
+
+@Component({
+	selector: 'cse-remove-course-confirm-dialog',
+	templateUrl: './remove-course-confirm-dialog.component.html',
+	styleUrls: ['./remove-course-confirm-dialog.component.scss']
+})
+export class RemoveCourseConfirmDialogComponent implements OnInit {
+	course: Course;
+	savedForLater: Boolean;
+	courses: Course[];
+	favoriteCourses: FavoriteCourse[];
+
+	// tslint:disable-next-line:max-line-length
+	constructor(private dataService: DataService, private dialogRef: MatDialogRef<RemoveCourseConfirmDialogComponent>, @Inject(MAT_DIALOG_DATA) data) {
+		this.course = data.course;
+		this.courses = data.courses;
+		this.favoriteCourses = data.favoriteCourses;
+		this.savedForLater = data.savedForLater;
+	}
+
+	ngOnInit() {
+	}
+
+	removeCourseFromUI(courseItem) {
+		const index = courseItem.indexOf(this.course, 0);
+		if (index > -1) {
+			courseItem = courseItem.splice(index, 1);
+		}
+	}
+
+	// Remove this course from the degree plan
+	removeCourse() {
+		if (this.savedForLater) {
+			this.dataService.removeFavoriteCourse(this.course.subjectCode, this.course.courseId)
+			.subscribe(data => {
+				this.removeCourseFromUI(this.favoriteCourses);
+			});
+		} else {
+			this.dataService.removeCourse(this.course.id)
+			.subscribe(data => {
+				this.removeCourseFromUI(this.courses);
+		});
+		}
+	}
+}
diff --git a/src/app/degree-planner/favorites-container/favorites-container.component.html b/src/app/degree-planner/favorites-container/favorites-container.component.html
index 728914f..6b69125 100644
--- a/src/app/degree-planner/favorites-container/favorites-container.component.html
+++ b/src/app/degree-planner/favorites-container/favorites-container.component.html
@@ -10,7 +10,11 @@
 			<div class="course-wrapper-inner">
 				<cse-course-item
 						[course]="course"
+						[favoriteCourses]="favoriteCourses"
 						[status]="'favorite'"
+						[savedForLater]="true"
+						[term]="term"
+						[termsByAcademicYear]="termsByAcademicYear"
 						[subject]="subjectsMap[course.subjectCode]"
 						class="course-favorite"
 				></cse-course-item>
diff --git a/src/app/degree-planner/favorites-container/favorites-container.component.ts b/src/app/degree-planner/favorites-container/favorites-container.component.ts
index 6c61167..f353e50 100644
--- a/src/app/degree-planner/favorites-container/favorites-container.component.ts
+++ b/src/app/degree-planner/favorites-container/favorites-container.component.ts
@@ -10,17 +10,13 @@ import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/dr
 })
 
 export class FavoritesContainerComponent implements OnInit {
-	favoriteCourses: FavoriteCourse[];
 	favoriteCourse: FavoriteCourse;
 	@Input() subjectsMap: Object;
 	@Input() favoriteDropZone: [];
+	@Input() termsByAcademicYear: Object;
+	@Input() favoriteCourses: FavoriteCourse[];
 
 	constructor(private dataService: DataService) {
-
-		this.dataService.getFavoriteCourses()
-		.subscribe(favoriteCourses => {
-			this.favoriteCourses = favoriteCourses;
-		});
 	}
 
 	ngOnInit() {
diff --git a/src/app/degree-planner/shared/course-item/course-item.component.html b/src/app/degree-planner/shared/course-item/course-item.component.html
index a5374f9..e610d35 100644
--- a/src/app/degree-planner/shared/course-item/course-item.component.html
+++ b/src/app/degree-planner/shared/course-item/course-item.component.html
@@ -1,21 +1,47 @@
 <div class="course-item {{status}} {{disabled ? 'disabled' : ''}}">
-  <div class="course-row course-info">
-    <div class="icon-number-wrapper">
-        <p class="course-number">
-            {{subject}} {{course.catalogNumber}}
-        </p>
-        <div [ngSwitch]="status">
-            <i *ngSwitchCase="'complete'" class="material-icons complete-icon">check_circle</i>
-            <i *ngSwitchCase="'in-progress'" class="material-icons in-progress-icon">check_circle</i>
-            <i *ngSwitchCase="'waitlist'" class="material-icons problem-icon">report_problem</i>
-            <i *ngSwitchCase="'incomplete'" class="material-icons error-icon">error</i>
-            <i *ngSwitchCase="'favorite'" class="material-icons favorite-icon">favorite_border</i>
-        </div>
-    </div>
-    <p class="course-credits">{{ course.credits !== undefined ? course.credits : "--" }} Cr</p>
-  </div>
 
-  <div class="course-row">
-    <p class="course-title">{{course.title}}</p>
-  </div>
+	<div fxLayout="row" fxLayoutAlign="space-between start">
+		<div fxLayout="column" fxLayoutAlign="space-between start" fxFlex="80" (click)="openCourseDetailsDialog(course)">
+			<div fxLayout="row" fxLayoutAlign="start center">
+				<div class="icon-number-wrapper">
+					<p class="course-number">
+						{{subject}} {{course.catalogNumber}}
+					</p>
+					<div [ngSwitch]="status">
+						<i *ngSwitchCase="'complete'" class="material-icons complete-icon">check_circle</i>
+						<i *ngSwitchCase="'in-progress'" class="material-icons in-progress-icon">check_circle</i>
+						<i *ngSwitchCase="'waitlist'" class="material-icons problem-icon">report_problem</i>
+						<i *ngSwitchCase="'incomplete'" class="material-icons error-icon">error</i>
+						<i *ngSwitchCase="'favorite'" class="material-icons favorite-icon">favorite_border</i>
+					</div>
+				</div>
+			</div>
+			<div fxLayout="row" fxLayoutAlign="start center">
+				<p class="course-title">{{course.title}}</p>
+			</div>
+		</div>
+
+		<div fxLayout="column" fxLayoutAlign="space-between end" fxFlex="20">
+			<div fxLayout="row" fxLayoutAlign="end center">
+				<mat-icon [matMenuTriggerFor]="courseMenu" (click)="getAllTermCodes()" aria-label="Course menu" matTooltip="Course Menu" matTooltipPosition="right" class="material-icons">more_horiz</mat-icon>
+				<mat-menu #courseMenu="matMenu" class="course-item-menu">
+					<button mat-menu-item (click)="openCourseDetailsDialog(course)">Course Details</button>
+					<button mat-menu-item [matMenuTriggerFor]="academicYearsGroup">Move</button>
+						<mat-menu #academicYearsGroup="matMenu" class="course-item-submenu">
+							<button mat-menu-item (click)="moveToFavorites(course)" *ngIf="status !== 'favorite'" class="favorites-list">Favorites list</button>
+							<button mat-menu-item *ngFor="let academicYear of termsByAcademicYear | keyvalue" [matMenuTriggerFor]="termsInAcademicYearGroup">
+								{{ academicYear.value.year | academicYearRange }}
+								<mat-menu #termsInAcademicYearGroup="matMenu" class="course-item-submenu">
+									<button mat-menu-item *ngFor="let term of academicYear.value.terms | keyvalue" (click)="status !== 'favorite' ? switchTerm(term.value.termCode) : addToTerm(term.value.termCode)">{{ term.value.termCode | getTermDescription }}</button>
+								</mat-menu>
+							</button>
+						</mat-menu>
+					<button mat-menu-item (click)="openRemoveConfirmationDialog(savedForLater)">Remove</button>
+				</mat-menu>
+			</div>
+			<div fxLayout="row" fxLayoutAlign="end center">
+				<p class="course-credits">{{ course.credits !== undefined ? course.credits : "--" }} Cr</p>
+			</div>
+		</div>
+	</div>
 </div>
diff --git a/src/app/degree-planner/shared/course-item/course-item.component.scss b/src/app/degree-planner/shared/course-item/course-item.component.scss
index bf3f1c8..344a1d4 100644
--- a/src/app/degree-planner/shared/course-item/course-item.component.scss
+++ b/src/app/degree-planner/shared/course-item/course-item.component.scss
@@ -36,14 +36,16 @@
         margin: 0;
         padding: 0;
     }
-}
-
-.course-row {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
 
-    width: 100%;
+    .mat-icon {
+        border-radius: 50%;
+        text-align: center;
+        font-size: 18px;
+        line-height: 1.4;
+        &:hover, &:focus {
+            background: #dadee0;
+        }
+    }
 }
 
 .course-number,
@@ -57,12 +59,6 @@
     display: inline-block;
 }
 
-.material-icons {
-    font-size: 16px;
-    margin-left: 10px;
-    padding-top: 6px;
-}
-
 .icon-number-wrapper {
     display: flex;
     align-items: center;
@@ -75,4 +71,15 @@
         text-decoration: line-through;
         opacity: .5;
     }
-}
\ No newline at end of file
+}
+
+button.mat-menu-item {
+    text-transform: none !important;
+}
+
+@media screen and (max-width: 957px) {
+    .mat-icon {
+        padding: 0.5em;
+        margin-bottom: 0.5em;
+    }
+}
diff --git a/src/app/degree-planner/shared/course-item/course-item.component.ts b/src/app/degree-planner/shared/course-item/course-item.component.ts
index 068f800..7d27009 100644
--- a/src/app/degree-planner/shared/course-item/course-item.component.ts
+++ b/src/app/degree-planner/shared/course-item/course-item.component.ts
@@ -1,17 +1,112 @@
-import { Component, Input } from '@angular/core';
+import { Component, Input, ChangeDetectorRef, OnInit } from '@angular/core';
 import { Course } from '../../../core/models/course';
+import { Term } from '../../../core/models/term';
+import { FavoriteCourse } from '../../../core/models/favorite-course';
+import { DataService } from '../../../core/data.service';
+import { CourseDetailsDialogComponent } from '../../dialogs/course-details-dialog/course-details-dialog.component';
+// tslint:disable-next-line:max-line-length
+import { RemoveCourseConfirmDialogComponent } from '../../dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component';
+import { MatDialog } from '@angular/material';
 
 @Component({
 	selector: 'cse-course-item',
 	templateUrl: './course-item.component.html',
 	styleUrls: ['./course-item.component.scss']
 })
-export class CourseItemComponent {
+
+export class CourseItemComponent implements OnInit {
+	termsInAcademicYear: [];
+	favoriteCourse: FavoriteCourse;
+	degreePlanCourses: any[];
 	@Input() course: Course;
+	@Input() courses: Course[];
 	@Input() status: string;
 	@Input() subject: string;
 	@Input() disabled: string;
+	@Input() term: Term;
+	@Input() termsByAcademicYear: Object;
+	@Input() refreshContent;
+	@Input() savedForLater: Boolean;
+	@Input() favoriteCourses: FavoriteCourse[];
+
+	constructor(
+		private dataService: DataService,
+		public dialog: MatDialog,
+		private cdRef: ChangeDetectorRef) {
+		}
+
+	ngOnInit() {
+	}
+
+	removeCourseFromUI(courseItem) {
+		const index = courseItem.indexOf(this.course, 0);
+		if (index > -1) {
+			courseItem = courseItem.splice(index, 1);
+		}
+	}
+
+	openCourseDetailsDialog(course) {
+		this.dataService.getCourseDetails(course.termCode, course.subjectCode, course.courseId)
+		.subscribe(courseDetails => {
+			const dialogRef = this.dialog.open(CourseDetailsDialogComponent, {
+				data: { courseDetails: courseDetails }
+			});
+		});
+	}
+
+	// Get term codes to display in the course item menu
+	getAllTermCodes() {
+		const termYears: any = Object.values(this.termsByAcademicYear);
+		this.termsInAcademicYear = termYears.map(year => year.terms).flat();
+	}
+
+	// Add this coruse to the favorites list
+	moveToFavorites(course) {
+		this.dataService.saveFavoriteCourse(course.subjectCode, course.courseId)
+		.subscribe(favoriteCourse => {
+			this.favoriteCourses.push(this.course);
+		});
+
+		// Remove this course from term
+		this.dataService.removeCourse(course.id)
+		.subscribe(data => {
+			this.removeCourseFromUI(this.courses);
+		});
+	}
+
+	// Add Favorite course to term
+	addToTerm(newTermCode) {
+		this.dataService.addCourse( this.course.subjectCode, this.course.courseId, newTermCode)
+		.subscribe( data => {
+			const yearCode = newTermCode.substring(0, 3);
+			const termCode = newTermCode.substring(3);
+			this.termsByAcademicYear[yearCode].terms[termCode].courses.push(this.course);
+		});
+
+		// Remove course from favorites list
+		this.dataService.removeFavoriteCourse(this.course.subjectCode, this.course.courseId)
+		.subscribe(data => {
+			this.removeCourseFromUI(this.favoriteCourses);
+		});
+	}
 
-	constructor() {}
+	// Move course to another term
+	switchTerm(newTermCode) {
+		this.dataService.updateCourseTerm(this.course.id, newTermCode)
+		.subscribe( data => {
+			const index = this.courses.indexOf(this.course, 0);
+			if (index > -1) {
+				this.courses = this.courses.splice(index, 1);
+				const yearCode = newTermCode.substring(0, 3);
+				const termCode = newTermCode.substring(3);
+				this.termsByAcademicYear[yearCode].terms[termCode].courses.push(this.course);
+			}
+		});
+	}
 
+	openRemoveConfirmationDialog(savedForLater) {
+		const dialogRef = this.dialog.open(RemoveCourseConfirmDialogComponent, {
+			data: { course: this.course, courses: this.courses, favoriteCourses: this.favoriteCourses, savedForLater }
+		});
+	}
 }
diff --git a/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.html b/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.html
index f7ffa7a..8d81fd2 100644
--- a/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.html
+++ b/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.html
@@ -20,7 +20,7 @@
 				<h3>Favorites</h3>
 			</mat-panel-title>
 		</mat-expansion-panel-header>
-		<cse-favorites-container [favoriteDropZone]='favoriteDropZone' [subjectsMap]="subjectsMap"></cse-favorites-container>
+		<cse-favorites-container [termsByAcademicYear]="termsByAcademicYear" [favoriteCourses]="favoriteCourses" [favoriteDropZone]='favoriteDropZone' [subjectsMap]="subjectsMap"></cse-favorites-container>
 	</mat-expansion-panel>
 
 	<mat-expansion-panel id="menu-items-container" expanded="true">
diff --git a/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.ts b/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.ts
index 9c32d92..3688091 100644
--- a/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.ts
+++ b/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.ts
@@ -10,6 +10,8 @@ export class SidenavMenuItemComponent implements OnInit {
 	opened: boolean;
 	@Input() favoriteDropZone: [];
 	@Input() subjectsMap: Object;
+	@Input() termsByAcademicYear: Object;
+	@Input() favoriteCourses;
 
 	constructor() { }
 
diff --git a/src/app/degree-planner/term-container/term-container.component.html b/src/app/degree-planner/term-container/term-container.component.html
index f7cac00..c65f919 100644
--- a/src/app/degree-planner/term-container/term-container.component.html
+++ b/src/app/degree-planner/term-container/term-container.component.html
@@ -1,41 +1,44 @@
 <mat-card class="term-container">
-	<div class="term-inner">
-		<div fxLayout="row" class="term-header" fxLayoutAlign="space-between center">
-			<h2>{{ term.termCode | getTermDescription }}</h2>
-			<div fxLayout="row" fxLayoutAlign="end center">
-				<p class="text-right semi-bold credits">{{getTotalCredits()}} Cr</p>
-				<button mat-icon-button>
-					<mat-icon aria-label="Open dialog with notes in this term" class="add-note-icon" (click)="openNotesDialog(false, term.termCode)" matTooltip="Add Note" matTooltipPosition="above">note_add</mat-icon>
-					<!-- <mat-icon aria-label="Open dialog with notes in this term" class="add-note-icon" (click)="openNotesDialog(false, term.termCode)" matTooltip="Edit Note" matTooltipPosition="above">insert_drive_file</mat-icon> -->
-				 </button>
-			</div>
-		</div>
-		<div
-			cdkDropList
-			id="term-{{term.termCode}}"
-			[cdkDropListData]="courses"
-			[cdkDropListConnectedTo]="termCodes"
-			class="course-list"
-			(cdkDropListDropped)="drop($event)">
-			<ng-container *ngFor="let note of notes | async">
-				<div *ngIf="note.termCode == term.termCode" class="note-item" (click)="openNotesDialog(note, term.termCode)" >
-					<p class="semi-bold">Note</p>
-					<p class="note-excerpt">{{ note.note }}</p>
-				</div>
-			</ng-container>
-			<div class="course-wrapper" [cdkDragData]="course" *ngFor="let course of courses" cdkDrag>
-				<div class="course-wrapper-inner">
-					<cse-course-item 
-						[course]="course" 
-						(click)="openCourseDetailsDialog(course)" 
-						[status]="'in-progress'"
-						[subject]="subjectsMap[course.subjectCode]"
-					></cse-course-item>
-				</div>
-			</div>
-		</div>
-	</div>
-	<div class="add-new-wrapper">
-		<button mat-raised-button (click)="openAddSidenav()">+ Add Course</button>
-	</div>
+    <div class="term-inner">
+        <div fxLayout="row" class="term-header" fxLayoutAlign="space-between center">
+            <h2>{{ term.termCode | getTermDescription }}</h2>
+            <div fxLayout="row" fxLayoutAlign="end center">
+                <p class="text-right semi-bold credits">{{getTotalCredits()}} Cr</p>
+                <button mat-icon-button>
+                    <mat-icon aria-label="Open dialog with notes in this term" class="add-note-icon" (click)="openNotesDialog(false, term.termCode)" matTooltip="Add Note" matTooltipPosition="above">note_add</mat-icon>
+                    <!-- <mat-icon aria-label="Open dialog with notes in this term" class="add-note-icon" (click)="openNotesDialog(false, term.termCode)" matTooltip="Edit Note" matTooltipPosition="above">insert_drive_file</mat-icon> -->
+                 </button>
+            </div>
+        </div>
+        <div
+            cdkDropList
+            id="term-{{term.termCode}}"
+            [cdkDropListData]="courses"
+            [cdkDropListConnectedTo]="termCodes"
+            class="course-list"
+            (cdkDropListDropped)="drop($event)">
+            <ng-container *ngFor="let note of notes | async">
+                <div *ngIf="note.termCode == term.termCode" class="note-item" (click)="openNotesDialog(note, term.termCode)" >
+                    <p class="semi-bold">Note</p>
+                    <p class="note-excerpt">{{ note.note }}</p>
+                </div>
+            </ng-container>
+            <div class="course-wrapper" [cdkDragData]="course" *ngFor="let course of courses" cdkDrag>
+                <div class="course-wrapper-inner">
+                    <cse-course-item 
+                        [course]="course"
+                        [courses]="courses"
+                        [term]="term"
+                        [favoriteCourses]="favoriteCourses"
+                        [termsByAcademicYear]="termsByAcademicYear"
+                        [status]="'in-progress'"
+                        [subject]="subjectsMap[course.subjectCode]"
+                    ></cse-course-item>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="add-new-wrapper">
+        <button mat-raised-button  (click)="openAddSidenav()">+ Add Course</button>
+    </div>
 </mat-card>
\ No newline at end of file
diff --git a/src/app/degree-planner/term-container/term-container.component.ts b/src/app/degree-planner/term-container/term-container.component.ts
index 474465d..318fb7c 100644
--- a/src/app/degree-planner/term-container/term-container.component.ts
+++ b/src/app/degree-planner/term-container/term-container.component.ts
@@ -26,6 +26,7 @@ export class TermContainerComponent {
 	@Input() course: CourseDetails;
 	@Input() subjectsMap: Object;
 	@Input() notes: Observable<Array<Note>>;
+	@Input() favoriteCourses;
 
 	terms: any[];
 	note: Object;
diff --git a/src/app/shared/academic-year-range.pipe.ts b/src/app/shared/academic-year-range.pipe.ts
new file mode 100644
index 0000000..851182b
--- /dev/null
+++ b/src/app/shared/academic-year-range.pipe.ts
@@ -0,0 +1,23 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+	name: 'academicYearRange'
+})
+export class AcademicYearRangePipe implements PipeTransform {
+
+	transform(yearCode: string, args?: any): any {
+		// Get the century digit
+		const century = parseInt(yearCode.substring(0, 1), 0) + 19;
+
+		// Get the year
+		const academicYear = parseInt(yearCode.substring(1), 0);
+
+		const endYear = (century * 100) + academicYear;
+		const startYear = endYear - 1;
+
+		const yearString = `${startYear} - ${endYear}`;
+
+		return yearString;
+	}
+
+}
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index c09a08e..f9c85f0 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -19,6 +19,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
 import { GetTermDescriptionPipe } from './get-term-description.pipe';
 import { AcademicYearStatePipe } from './academic-year-state.pipe';
 import { CourseDetailsComponent } from './course-details/course-details.component';
+import { AcademicYearRangePipe } from './academic-year-range.pipe';
 
 const modules = [
 	CommonModule,
@@ -41,8 +42,8 @@ const modules = [
 
 @NgModule({
 	imports: [ modules ],
-	exports: [ modules, GetTermDescriptionPipe, AcademicYearStatePipe, CourseDetailsComponent ],
-	declarations: [ GetTermDescriptionPipe, AcademicYearStatePipe, CourseDetailsComponent ]
+	exports: [ modules, GetTermDescriptionPipe, AcademicYearStatePipe, CourseDetailsComponent, AcademicYearRangePipe ],
+	declarations: [ GetTermDescriptionPipe, AcademicYearStatePipe, CourseDetailsComponent, AcademicYearRangePipe ]
 })
 
 export class SharedModule { }
diff --git a/src/assets/sass/general.scss b/src/assets/sass/general.scss
index 3434a39..fded82f 100644
--- a/src/assets/sass/general.scss
+++ b/src/assets/sass/general.scss
@@ -44,6 +44,10 @@ body {
     border-radius: 4px !important;
 }
 
+.btn-link {
+    color: map-get($uw-primary, 500) !important;
+}
+
 .btn-delete {
     background-color: #b5261e;
     color: white;
@@ -73,6 +77,18 @@ body {
     color: map-get($uw-accent, 600);
 }
 
+.complete-icon,
+.in-progress-icon,
+.problem-icon,
+.error-icon,
+.help-icon,
+.favorite-icon,
+.not-offered {
+    font-size: 16px !important;
+    margin-left: 5px;
+    padding-top: 5px;
+}
+
 #favoriteCourse-dropZone {
     .course-favorite {
         .course-item {
@@ -141,6 +157,17 @@ body {
 	}
 }
 
+// Menus styles
+
+.course-item-menu,
+.mat-menu-content {
+    width: 200px !important;
+}
+
+.favorites-list {
+    border-bottom: 1px solid #f1f1f1 !important;
+}
+
 // Dialogs styles
 
 .mat-dialog-container {
-- 
GitLab