From ed8eea4b4b718e284277b16d86d540dc6357485c Mon Sep 17 00:00:00 2001
From: pnogal <paulina.nogal@wisc.edu>
Date: Wed, 14 Nov 2018 11:19:32 -0600
Subject: [PATCH] Add and remove course from favorites

---
 src/app/core/data.service.ts                  | 25 +++++++++++-
 src/app/core/models/favorite-course.ts        | 10 +++++
 .../degree-planner.component.html             |  4 +-
 .../degree-planner.component.ts               |  4 +-
 .../favorites-container.component.html        | 19 ++++++++--
 .../favorites-container.component.ts          | 30 ++++++++++++++-
 .../sidenav-menu-item.component.html          |  2 +-
 .../sidenav-menu-item.component.scss          |  1 +
 .../sidenav-menu-item.component.ts            |  3 +-
 .../term-container.component.html             |  6 +--
 .../term-container.component.scss             | 26 -------------
 .../term-container.component.ts               | 10 ++++-
 src/assets/sass/general.scss                  | 38 ++++++++++++++++++-
 13 files changed, 134 insertions(+), 44 deletions(-)
 create mode 100644 src/app/core/models/favorite-course.ts

diff --git a/src/app/core/data.service.ts b/src/app/core/data.service.ts
index 15ebcc5..9d94cd2 100644
--- a/src/app/core/data.service.ts
+++ b/src/app/core/data.service.ts
@@ -1,12 +1,18 @@
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
 import { Injectable } from '@angular/core';
-import { HttpErrorResponse } from '@angular/common/http';
 import { throwError, Observable } from 'rxjs';
 import { catchError, map } from 'rxjs/operators';
 import { ConfigService } from './config.service';
 import { Course } from './models/course';
 import { DegreePlan } from './models/degree-plan';
 import { Term } from './models/term';
+import { FavoriteCourse } from './models/favorite-course';
+
+const httpOptions = {
+	headers: new HttpHeaders({
+		'Content-Type':  'application/json'
+	})
+};
 
 @Injectable()
 export class DataService {
@@ -33,6 +39,21 @@ export class DataService {
 			.pipe(catchError(this.errorHandler));
 	}
 
+	getFavoriteCourses(): Observable<FavoriteCourse[]> {
+		return this.http.get<FavoriteCourse[]>(this.plannerApiUrl + '/favorites')
+			.pipe(catchError(this.errorHandler));
+	}
+
+	saveFavoriteCourse(subjectCode: string, courseId: string): Observable<FavoriteCourse> {
+		return this.http.post<FavoriteCourse>(this.plannerApiUrl + 'favorites/' + subjectCode + '/' + courseId, httpOptions)
+		.pipe(catchError(this.errorHandler));
+	}
+
+	removeFavoriteCourse(subjectCode, courseId): Observable<FavoriteCourse> {
+		return this.http.delete<FavoriteCourse>(this.plannerApiUrl + 'favorites/' + subjectCode + '/' + courseId, httpOptions)
+		.pipe(catchError(this.errorHandler));
+	}
+
 	private errorHandler(error: HttpErrorResponse) {
 		return throwError(error || 'Server Error');
 	}
diff --git a/src/app/core/models/favorite-course.ts b/src/app/core/models/favorite-course.ts
new file mode 100644
index 0000000..45c1db7
--- /dev/null
+++ b/src/app/core/models/favorite-course.ts
@@ -0,0 +1,10 @@
+export interface FavoriteCourse {
+	id: number;
+	courseId: string;
+	termCode: string;
+	topicId: number;
+	subjectCode: string;
+	title: string;
+	catalogNumber: string;
+	courseOrder: number;
+}
diff --git a/src/app/degree-planner/degree-planner.component.html b/src/app/degree-planner/degree-planner.component.html
index 3597744..7797147 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 #rightSideNav position="end" mode="side" [opened]="true" disableClose>
-		<cse-sidenav-menu-item></cse-sidenav-menu-item>
+		<cse-sidenav-menu-item [favoriteDropZone]="getTermDropZone()"></cse-sidenav-menu-item>
 	</mat-sidenav>
 
 	<!-- Add course sidenav -->
@@ -46,7 +46,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']" 
-									[termCodes]="getTermCodes()"
+									[termCodes]="getTermDropZone()"
 									fxFlex="33%"
 								></cse-term-container>
 							</div>
diff --git a/src/app/degree-planner/degree-planner.component.ts b/src/app/degree-planner/degree-planner.component.ts
index 0d579c3..c182c2c 100644
--- a/src/app/degree-planner/degree-planner.component.ts
+++ b/src/app/degree-planner/degree-planner.component.ts
@@ -95,12 +95,12 @@ export class DegreePlannerComponent {
 		return [];
 	}
 
-	getTermCodes() {
+	getTermDropZone() {
 		if (!this.degreePlanCourses) {
 			return false;
 		}
 
-		const termCodes = [];
+		const termCodes = ['favoriteCourse-dropZone'];
 
 		for (const yearCode in this.termsByAcademicYear) {
 			if (this.termsByAcademicYear[yearCode]) {
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 f4d0423..9c9ef4d 100644
--- a/src/app/degree-planner/favorites-container/favorites-container.component.html
+++ b/src/app/degree-planner/favorites-container/favorites-container.component.html
@@ -1,3 +1,16 @@
-<div id="favorite-container">
-  My favorite courses!
-</div>
\ No newline at end of file
+<div class="term-container">
+	<div
+		cdkDropList
+		id="favoriteCourse-dropZone"
+		class="course-list"
+		[cdkDropListData]="favoriteCourses"
+		[cdkDropListConnectedTo]="favoriteDropZone"
+		(cdkDropListDropped)="drop($event)">
+		<div class="course-wrapper" [cdkDragData]="course" *ngFor="let course of favoriteCourses" cdkDrag>
+			<div class="course-wrapper-inner">
+				<cse-course-item [course]="course" [status]="'favorite'" class="course-favorite"></cse-course-item>
+			</div>
+		</div>
+		<p *ngIf="!favoriteCourses || favoriteCourses.length === 0" class="no-courses text-center semi-bold">No Favorite Courses</p>
+	</div>
+</div>
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 12a3734..e3f3756 100644
--- a/src/app/degree-planner/favorites-container/favorites-container.component.ts
+++ b/src/app/degree-planner/favorites-container/favorites-container.component.ts
@@ -1,15 +1,41 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, Input } from '@angular/core';
+import { DataService } from '../../core/data.service';
+import { FavoriteCourse } from '../../core/models/favorite-course';
+import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
 
 @Component({
 	selector: 'cse-favorites-container',
 	templateUrl: './favorites-container.component.html',
 	styleUrls: ['./favorites-container.component.scss']
 })
+
 export class FavoritesContainerComponent implements OnInit {
+	favoriteCourses: FavoriteCourse[];
+	favoriteCourse: FavoriteCourse;
+	@Input() favoriteDropZone: [];
+
+	constructor(private dataService: DataService) {
 
-	constructor() { }
+		this.dataService.getFavoriteCourses()
+		.subscribe(favoriteCourses => {
+			this.favoriteCourses = favoriteCourses;
+		});
+	}
 
 	ngOnInit() {
 	}
 
+	drop(event: CdkDragDrop<string[]>) {
+		if (event.previousContainer.id !== event.container.id) {
+			transferArrayItem(event.previousContainer.data,
+				event.container.data,
+				event.previousIndex,
+				event.currentIndex);
+		}
+
+		this.dataService.saveFavoriteCourse(event.item.data.subjectCode, event.item.data.courseId)
+		.subscribe(favoriteCourse => {
+			this.favoriteCourse = favoriteCourse;
+		});
+	}
 }
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 a5d771b..3a5596b 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
@@ -37,6 +37,6 @@
 				<h3>Favorites</h3>
 			</mat-panel-title>
 		</mat-expansion-panel-header>
-		<cse-favorites-container></cse-favorites-container>
+		<cse-favorites-container [favoriteDropZone]='favoriteDropZone'></cse-favorites-container>
 	</mat-expansion-panel>
 </div>
\ No newline at end of file
diff --git a/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.scss b/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.scss
index 41019fe..1c34884 100644
--- a/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.scss
+++ b/src/app/degree-planner/sidenav-menu-item/sidenav-menu-item.component.scss
@@ -5,6 +5,7 @@
     height: 100vh;
     .mat-expansion-panel {
         border-radius: 0px;
+        overflow: visible;
     }
     h3 {
         font-weight: 400;
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 eb57284..185f821 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
@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, Input } from '@angular/core';
 
 @Component({
 	selector: 'cse-sidenav-menu-item',
@@ -8,6 +8,7 @@ import { Component, OnInit } from '@angular/core';
 export class SidenavMenuItemComponent implements OnInit {
 	events: string[] = [];
 	opened: boolean;
+	@Input() favoriteDropZone: [];
 
 	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 ad65b81..018385c 100644
--- a/src/app/degree-planner/term-container/term-container.component.html
+++ b/src/app/degree-planner/term-container/term-container.component.html
@@ -9,9 +9,9 @@
         [cdkDropListConnectedTo]="termCodes"
         class="course-list"
         (cdkDropListDropped)="drop($event)">
-        <div class="course-wrapper" *ngFor="let course of courses" cdkDrag>
-            <div class="coure-wrapper-inner">
-                <cse-course-item [course]="course" [status]="'complete'"></cse-course-item>
+        <div class="course-wrapper" [cdkDragData]="course" *ngFor="let course of courses" cdkDrag>
+            <div class="course-wrapper-inner">
+                <cse-course-item [course]="course" [status]="'in-progress'"></cse-course-item>
             </div>
         </div>
         <p *ngIf="!courses || courses.length === 0" class="no-courses text-center semi-bold">No Courses Taken</p>
diff --git a/src/app/degree-planner/term-container/term-container.component.scss b/src/app/degree-planner/term-container/term-container.component.scss
index 1c1e108..2174fba 100644
--- a/src/app/degree-planner/term-container/term-container.component.scss
+++ b/src/app/degree-planner/term-container/term-container.component.scss
@@ -11,29 +11,3 @@
 .term-container h2 {
 	margin: 0 0 20px 0;
 }
-
-.no-courses {
-	padding: 20px 10px;
-}
-
-.cdk-drag-placeholder {
-	position: relative;
-	&:after {
-		content: '';
-		position: absolute;
-		top: 0;
-		right: 0;
-		bottom: 0;
-		left: 0;
-		background-color: #C7CACB;
-		border-radius: 5px;
-	}
-}
-
-.cdk-drag-preview {
-	.coure-wrapper-inner {
-		transform: rotate(-3deg);
-		border-radius: 5px;
-		box-shadow: -3px 3px 3px 2px rgba(0, 0, 0, 0.26);
-	}
-}
\ 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 41c8864..9e007ca 100644
--- a/src/app/degree-planner/term-container/term-container.component.ts
+++ b/src/app/degree-planner/term-container/term-container.component.ts
@@ -1,6 +1,8 @@
 import { Component, Input } from '@angular/core';
 import { Term } from '../../core/models/term';
 import { Course } from '../../core/models/course';
+import { FavoriteCourse } from '../../core/models/favorite-course';
+import { DataService } from '../../core/data.service';
 import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
 
 @Component({
@@ -14,8 +16,9 @@ export class TermContainerComponent {
 	@Input() courses: Course[];
 	@Input() termCodes: String[];
 	terms: any[];
+	favoriteCourse: FavoriteCourse;
 
-	constructor() {}
+	constructor(private dataService: DataService) {}
 
 	getTotalCredits() {
 		if (!this.courses) {
@@ -36,5 +39,10 @@ export class TermContainerComponent {
 				event.previousIndex,
 				event.currentIndex);
 		}
+
+		this.dataService.removeFavoriteCourse(event.item.data.subjectCode, event.item.data.courseId)
+		.subscribe(favoriteCourse => {
+			this.favoriteCourse = favoriteCourse;
+		});
 	}
 }
diff --git a/src/assets/sass/general.scss b/src/assets/sass/general.scss
index 905649d..5e78ea7 100644
--- a/src/assets/sass/general.scss
+++ b/src/assets/sass/general.scss
@@ -49,4 +49,40 @@ body {
 
 .favorite-icon {
     color: map-get($uw-accent, 600);
-}
\ No newline at end of file
+}
+
+#favoriteCourse-dropZone {
+    .course-favorite {
+        .course-item {
+            border: 1px solid #b7b7b7;
+            border-radius: 4px;
+            padding: 5px 10px;
+        }
+    }
+}
+
+.cdk-drag-placeholder {
+	position: relative;
+	&:after {
+		content: '';
+		position: absolute;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		left: 0;
+		background-color: #C7CACB;
+		border-radius: 5px;
+	}
+}
+
+.cdk-drag-preview {
+	.course-wrapper-inner {
+		transform: rotate(-3deg);
+		border-radius: 5px;
+		box-shadow: -3px 3px 3px 2px rgba(0, 0, 0, 0.26);
+	}
+}
+
+.no-courses {
+	padding: 20px 10px;
+}
-- 
GitLab