From a9ee3c4afd7424f69c85d77664c0cb611dea8c37 Mon Sep 17 00:00:00 2001
From: pnogal <paulina.nogal@wisc.edu>
Date: Wed, 6 Feb 2019 13:56:24 -0600
Subject: [PATCH] Include Typeahead api

---
 src/app/app.component.html                    | 184 ++++++------------
 src/app/app.component.scss                    |   9 +-
 src/app/app.component.ts                      |  91 ++++++---
 src/app/app.module.ts                         |   2 +
 src/app/core/data.service.ts                  | 141 ++++++++++++++
 .../degree-planner/degree-planner.module.ts   |  33 ++--
 src/app/shared/shared.module.ts               |   9 +-
 7 files changed, 282 insertions(+), 187 deletions(-)
 create mode 100644 src/app/core/data.service.ts

diff --git a/src/app/app.component.html b/src/app/app.component.html
index 9e8cf98..01dffad 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,138 +1,64 @@
 <header>
-        <cse-header></cse-header>
-        <cse-navigation></cse-navigation>
+	<cse-header></cse-header>
+	<cse-navigation></cse-navigation>
 </header>
-	<main>
-		<mat-sidenav-container class="example-container" hasBackdrop="false" style="height: 100vh;">
-			<mat-sidenav mode="over" position="end" #rightAddCourse>
-				<mat-sidenav-container>
-					<mat-sidenav mode="side" position="start" #rightAddCourse2 class="course-details-pane">
-						<mat-toolbar color="primary" class="dialog-toolbar">
-							<button mat-button class="close-btn" (click)="rightAddCourse2.close()"><i class="material-icons">clear</i></button>
-							<span class="dialog-toolbar-title">Add course to degree plan</span>
-						</mat-toolbar>
-						<div id="course-details-content" class="mat-typography">
-							<div fxLayout="row">
-								<div fxLayout="row" fxLayout.lt-md="column" fxFlex="100" fxLayoutGap="10px" class="course-details-header">
-									<div fxFlex="50" class="course-detail-title" fxLayoutAlign="start start">
-										<h3>English 162<span class="course-detail-subtitle">Shakespeare</span></h3>
-									</div>
-									<div fxFlex="50" class="course-detail-title" fxLayoutAlign="end start">
-										<button mat-raised-button class="btn-primary mat-button" aria-label="Add course to plan">Add course</button>
-									</div>
-								</div>
-							</div>
-							<div>
-								<p>Introduction to several of Shakespare's most popular plays and their relation to other works of English and American literature.</p>
-								<p><span class="semi-bold">Enroll Info: </span>None</p>
-								<p><span class="semi-bold">Requisites: </span>None</p>
-								<ul class="courseDetails-list">
-									<li><span class="semi-bold">Credits:</span> 3</li>
-									<li><span class="semi-bold">Level: </span>
-										<span>Elementary</span>
-									</li>
-									<li><span class="semi-bold">Breadth: </span>
-										<span>Literature</span>
-									</li>
-									<li><span class="semi-bold">L&amp;S Credit Type:</span>
-										Counts as LAS credit (L&S)
-									</li>
-								</ul>
-								<ul class="courseDetails-list">
-									<li><span class="semi-bold">Last Taught: </span>Fall 2018</li>
-									<li><span class="semi-bold">Typically Offered: </span>Fall, Spring, Summer</li>
-								</ul>
+<main>
+	<mat-sidenav-container class="example-container" hasBackdrop="false" style="height: 100vh;">
+		<mat-sidenav mode="over" position="end" #rightAddCourse id="course-search-sidenav">
+			<mat-toolbar color="primary" class="dialog-toolbar">
+				<span class="dialog-toolbar-title">Course Search</span>
+				<button mat-button class="close-btn" (click)="rightAddCourse.close();"><i class="material-icons">clear</i></button>
+			</mat-toolbar>
 
-								<p><span class="semi-bold">Subject Notes:</span><br>
-								<span class="subject-notes"></span></p>
-							</div>
+			<div [formGroup]='coursesForm' class="add-course-form" fxLayout="column" fxLayoutAlign="space-around none" style="padding: 12px 22px;">
+				<mat-form-field>
+					<input type="text" placeholder="Term" aria-label="Term" matInput [formControl]="" [matAutocomplete]="term">
+					<mat-autocomplete #term="matAutocomplete">
+						<mat-option *ngFor="let term of (coursesData$ | async)" [value]="term[0].termCode | getTermDescription">
+							{{ term[0].termCode | getTermDescription }}
+						</mat-option>
+					</mat-autocomplete>
+				</mat-form-field>
 
-							<div class="course-details-footer">
-								<p class="semi-bold course-detail-title">English Information:</p>
-								<div fxLayout="row">
-									<div fxLayout="row" fxFlex="100" fxLayoutGap="10px">
-										<div fxFlex="100" fxLayout.lt-sm="column" class="mat-dialog-actions" fxLayoutAlign="start center" fxLayoutAlign.lt-sm="start start">
-											<a class="md-primary btn-link mat-button" href="">Website</a>
-											<a class="md-primary btn-link mat-button" href="">Undergraduate Info</a>
-											<a class="md-primary btn-link mat-button" href="">Graduate Info</a>
-										</div>
-									</div>
-								</div>
-							</div>
-						</div>
-					</mat-sidenav>
-					<mat-sidenav-content class="course-search-pane">
-						<mat-toolbar color="primary" class="dialog-toolbar">
-							<span class="dialog-toolbar-title">Course Search</span>
-							<button mat-button class="close-btn" (click)="rightAddCourse.close(); rightAddCourse2.close()"><i class="material-icons">clear</i></button>
-						</mat-toolbar>
-
-						<form class="add-course-form" fxLayout="column" fxLayoutAlign="space-around none" style="padding: 12px 22px;">
-							<mat-form-field>
-								<input type="text" placeholder="Term" aria-label="Term" matInput [formControl]="" [matAutocomplete]="term">
-								<mat-autocomplete #term="matAutocomplete">
-									<mat-option *ngFor="let term of coursesData$ | async" [value]="term[0].termCode | getTermDescription">
-										{{ term[0].termCode | getTermDescription }}
-									</mat-option>
-								</mat-autocomplete>
-							</mat-form-field>
-
-							<mat-form-field>
-								<input type="text" placeholder="Subject" aria-label="Subject" matInput [formControl]="" [matAutocomplete]="subject">
-								<mat-autocomplete #subject="matAutocomplete">
-									<mat-option>Afro-American Studies</mat-option>
-									<mat-option>Agricultural and Applied Economics</mat-option>
-									<mat-option>Agroecology</mat-option>
-									<mat-option>Agronomy</mat-option>
-									<mat-option>Air Force Aerospace Studies</mat-option>
-									<mat-option>American Indian Studies</mat-option>
-									<mat-option>Anatomy</mat-option>
-									<mat-option>Anatomy & Physiology</mat-option>
-								</mat-autocomplete>
-									<!-- <mat-error *ngIf="terms.hasError('required')">Please select an existing subject or 'All'.</mat-error> -->
-							</mat-form-field>
-
-							<mat-form-field class="example-full-width">
-								<input matInput placeholder="Subject, number" value="">
-							</mat-form-field>
-						</form>
-
-						<div class="search-results-toolbar mat-typography" fxLayout="row" fxLayoutAlign="space-between center" style="padding: 12px 22px; background-color: #EDF1F3">
-							<h3 style="margin: 0px;">7 of 7 results</h3>
-							<span class="mat-button">Reset Search</span>
-						</div>
+				<mat-form-field>
+					<input matInput placeholder="Subject" [matAutocomplete]="auto" formControlName='coursesInput' required>
+					<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
+						<mat-option *ngFor="let course of (courses | async)" [value]="course.textSuggest">
+							<span>{{ course.textSuggest }} </span>
+						</mat-option>
+					</mat-autocomplete>
+					<mat-error *ngIf="coursesInput.invalid">Please select an existing Subject or 'All'.</mat-error>
+				</mat-form-field>
+				<mat-form-field class="example-full-width">
+					<input matInput placeholder="Keyword, number" value="">
+				</mat-form-field>
 
-						<div id="course-search-results"  fxLayout="column" fxLayoutAlign="space-around none" style="padding: 12px 22px;">
-							<mat-form-field>
-								<mat-select placeholder="Order by">
-									<mat-option>Relevance</mat-option>
-									<mat-option>Subject</mat-option>
-									<mat-option>Catalog Number</mat-option>
-								</mat-select>
-							</mat-form-field>
+				<div class="search-results-toolbar mat-typography" fxLayout="row" fxLayoutAlign="space-between center" style="padding: 12px 22px; background-color: #EDF1F3">
+					<h3 style="margin: 0px;">15 results</h3>
+					<span class="mat-button">Reset Search</span>
+				</div>
 
-							<cse-course-item (click)="rightAddCourse2.toggle()">
-								<div class="course-item">
-									<p class="course-number">ENGL 162</p>
-									<p class="course-title">Shakespeare</p>
-								</div>
-								<div class="course-item">
-									<p class="course-number">PHILOS 101</p>
-									<p class="course-title">Introduction to Philosophy</p>
-								</div>
-								<div class="course-item">
-									<p class="course-number">Math 135</p>
-									<p class="course-title">Algebraic Reasoning for Teaching Math</p>
-								</div>
-							</cse-course-item>
+				<div id="course-search-results" fxLayout="column" fxLayoutAlign="space-around none" style="padding: 12px 22px;">
+					<mat-form-field>
+						<mat-select placeholder="Order by">
+							<mat-option>Relevance</mat-option>
+							<mat-option>Subject</mat-option>
+							<mat-option>Catalog Number</mat-option>
+						</mat-select>
+					</mat-form-field>
+					<cse-course-item *ngFor="let course of (courses | async)" (click)="openCourseDetailsDialog(course)">
+						<div class="course-item">
+							<p class="course-number">{{ course.payload.subject.shortDescription }} {{ course.payload.catalogNumber }}</p>
+							<p class="course-title">{{ course.textSuggest }}</p>
 						</div>
-					</mat-sidenav-content>
-				</mat-sidenav-container>
-			</mat-sidenav>
+					</cse-course-item>
+				</div>
+			</div>
+		</mat-sidenav>
 
-			<mat-sidenav-content>
-				<router-outlet></router-outlet>
-			</mat-sidenav-content>
+		<mat-sidenav-content>
+			<router-outlet></router-outlet>
+		</mat-sidenav-content>
 
-		</mat-sidenav-container>
-	</main>
+	</mat-sidenav-container>
+</main>
\ No newline at end of file
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index 99fcee6..114936e 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -20,13 +20,8 @@ main {
     overflow: hidden;
 }
 
-#course-details-content {
-    padding: 0 25px 25px 25px;
-}
-
-.course-search-pane,
-.course-details-pane {
+#course-search-sidenav {
     max-width: 360px;
     min-width: 360px;
     height: 100vh;
-}
+}
\ No newline at end of file
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 41e6b47..3ed5956 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,48 +1,75 @@
-import { SidenavService } from './core/service/sidenav.service';
 import { DataService } from './core/data.service';
+import { FormBuilder, FormGroup } from '@angular/forms';
+import { SidenavService } from './core/service/sidenav.service';
 import { Component, ViewChild, OnInit } from '@angular/core';
 import { MatSidenav } from '@angular/material';
 import { ActivatedRoute } from '@angular/router';
 import { DegreePlannerDataService } from './core/service/degree-planner-data.service';
-// import { FormBuilder, FormGroup } from '@angular/forms';
+import { Observable } from 'rxjs';
+import { debounceTime, switchMap, tap } from 'rxjs/operators';
+import { MatDialog } from '@angular/material';
+import { CourseDetailsDialogComponent } from './degree-planner/dialogs/course-details-dialog/course-details-dialog.component';
+import { ErrorStateMatcher } from '@angular/material/core';
+import { FormControl, FormGroupDirective, NgForm, Validators } from '@angular/forms';
 
 @Component({
-  selector: 'cse-root',
-  templateUrl: './app.component.html',
-  styleUrls: ['./app.component.scss'],
+    selector: 'cse-root',
+    templateUrl: './app.component.html',
+    styleUrls: ['./app.component.scss']
 })
+
 export class AppComponent implements OnInit {
-  @ViewChild('rightAddCourse') public rightAddCourse: MatSidenav;
-  constructor(private sidenavService: SidenavService) {}
+    coursesData$: any;
+    selectedDegreePlan: number;
+    courses: Observable<any>;
+    coursesForm: FormGroup;
+    coursesInput: any;
 
-  ngOnInit() {
-    this.sidenavService.setSidenav(this.rightAddCourse);
-  }
-	coursesData$: any;
-	selectedDegreePlan: number;
+    @ViewChild('rightAddCourse') public rightAddCourse: MatSidenav;
+    constructor(
+        public dialog: MatDialog,
+        private dataService: DataService,
+        private route: ActivatedRoute,
+        private sidenavService: SidenavService,
+        private degreePlannerDataSvc: DegreePlannerDataService,
+        private fb: FormBuilder) {
+            this.coursesInput = new FormControl('', [Validators.required]);
+            this.selectedDegreePlan = 520224;
+            this.coursesData$ = this.degreePlannerDataSvc.getDegreePlanDataById(this.selectedDegreePlan);
+    }
 
-	@ViewChild('rightAddCourse') public rightAddCourse: MatSidenav;
-	constructor(
-		private dataService: DataService,
-		private route: ActivatedRoute,
-		private sidenavService: SidenavService,
-		private degreePlannerDataSvc: DegreePlannerDataService) {
-		this.selectedDegreePlan = 520224;
-		this.coursesData$ = this.degreePlannerDataSvc.getDegreePlanDataById(this.selectedDegreePlan);
-	}
+    ngOnInit() {
+        this.sidenavService.setSidenav(this.rightAddCourse);
+        this.coursesForm = this.fb.group({
+                coursesInput: null
+        });
+        this.courses = this.coursesForm.get('coursesInput').valueChanges
+            .pipe(
+                debounceTime(300),
+                switchMap(value => this.dataService.autocomplete(value)),
+                tap(x => console.log( x))
+            );
+    }
 
-	ngOnInit() {
-		this.sidenavService.setSidenav(this.rightAddCourse);
-	}
+    openCourseDetailsDialog(course) {
+        this.dataService.getCourseDetails(course.termCode, course.payload.subject.subjectCode, course.payload.courseId)
+        .subscribe(courseDetails => {
+            const dialogRef = this.dialog.open(CourseDetailsDialogComponent, {
+                data: { courseDetails: courseDetails }
+            });
+        });
+    }
+
+}
 
 }
 document.addEventListener('WebComponentsReady', function() {
-  const customEvent = new CustomEvent('myuw-login', {
-    detail: {
-      person: {
-        // 'firstName': 'Bucky'
-      },
-    },
-  });
-  document.dispatchEvent(customEvent);
+    const customEvent = new CustomEvent('myuw-login', {
+        detail: {
+            person: {
+            // 'firstName': 'Bucky'
+            }
+        }
+    });
+    document.dispatchEvent(customEvent);
 });
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index b7d80fb..5188be0 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -16,6 +16,7 @@ import { degreePlannerReducer } from '@app/degree-planner/reducer';
 import { DegreePlanEffects } from '@app/degree-planner/effects/plan.effects';
 import { NoteEffects } from '@app/degree-planner/effects/note.effects';
 import { MatAutocompleteModule } from '@angular/material/autocomplete';
+import { CourseDetailsDialogComponent } from './degree-planner/dialogs/course-details-dialog/course-details-dialog.component';
 
 @NgModule({
 	imports: [
@@ -38,6 +39,7 @@ import { MatAutocompleteModule } from '@angular/material/autocomplete';
 		AppComponent,
 		HeaderComponent
 	],
+	entryComponents: [CourseDetailsDialogComponent],
 	providers: [ SidenavService ],
 	bootstrap: [ AppComponent ],
 	schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
diff --git a/src/app/core/data.service.ts b/src/app/core/data.service.ts
new file mode 100644
index 0000000..51eef86
--- /dev/null
+++ b/src/app/core/data.service.ts
@@ -0,0 +1,141 @@
+import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { throwError, Observable, forkJoin } 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 { SavedForLaterCourse } from './models/saved-for-later-course';
+import { CourseDetails } from './models/course-details';
+import { Note } from './models/note';
+
+const httpOptions = {
+	headers: new HttpHeaders({
+		'Content-Type':  'application/json'
+	})
+};
+
+@Injectable()
+export class DataService {
+	private plannerApiUrl: string;
+	private searchApiUrl: string;
+
+	constructor(private http: HttpClient, private configService: ConfigService) {
+		this.plannerApiUrl = this.configService.apiPlannerUrl;
+		this.searchApiUrl = this.configService.apiSearchUrl;
+	}
+
+	getAllPlanData(roadmapId: number) {
+		return forkJoin(
+			this.getDegreePlannerCourseData(roadmapId),
+			this.getTerms()
+		);
+	}
+
+	getDegreePlans(): Observable<DegreePlan[]> {
+		return this.http.get<DegreePlan[]>(this.plannerApiUrl + '/degreePlan')
+			.pipe(catchError(this.errorHandler));
+	}
+
+	getDegreePlannerCourseData(roadmapId: number): Observable<Course[]> {
+		return this.http.get<Course[]>(this.plannerApiUrl + '/degreePlan/' + roadmapId + '/courses')
+			.pipe(catchError(this.errorHandler));
+	}
+
+	getDegreePlannerCourseData2(roadmapId: number): Observable<Course> {
+		return this.http.get<Course>(this.plannerApiUrl + '/degreePlan/' + roadmapId + '/courses')
+			.pipe(catchError(this.errorHandler));
+	}
+
+	getTerms(): Observable<Term[]> {
+		return this.http.get<Term[]>(this.searchApiUrl + '/terms')
+			.pipe(catchError(this.errorHandler));
+	}
+
+	getCourseDetails(termCode: string, subjectCode: string, courseId: string): Observable<CourseDetails[]> {
+		return this.http.get<CourseDetails[]>(this.searchApiUrl + '/course/0000/' + subjectCode + '/' + courseId, httpOptions)
+			.pipe(catchError(this.errorHandler));
+	}
+
+	getSubjectsMap(): Observable<Object> {
+		return this.http.get(this.searchApiUrl + '/subjectsMap/0000', httpOptions)
+		.pipe(catchError(this.errorHandler));
+	}
+
+	getFavoriteCourses(): Observable<SavedForLaterCourse[]> {
+		return this.http.get<SavedForLaterCourse[]>(this.plannerApiUrl + '/favorites')
+			.pipe(catchError(this.errorHandler));
+	}
+
+	getAllNotes(planId: number): Observable<Note[]> {
+		return this.http.get<Note[]>(this.plannerApiUrl + '/degreePlan/' + planId + '/notes')
+			.pipe(catchError(this.errorHandler));
+	}
+
+	getNote(planId, noteId): Observable<Note[]> {
+		return this.http.get<Note[]>(this.plannerApiUrl + '/degreePlan/' + planId + '/notes/' + noteId)
+			.pipe(catchError(this.errorHandler));
+	}
+
+	updateNote(planId, note): Observable<Note[]> {
+		return this.http.put<Note[]>(this.plannerApiUrl + '/degreePlan/' + planId + '/notes/' + note.id, note, httpOptions)
+			.pipe(catchError(this.errorHandler));
+	}
+
+	createNote(planId, note): Observable<Note[]> {
+		return this.http.post<Note[]>(this.plannerApiUrl + '/degreePlan/' + planId + '/notes/', note, httpOptions)
+		.pipe(catchError(this.errorHandler));
+	}
+
+	removeNote(planId, noteId): Observable<Note[]> {
+		return this.http.delete<Note[]>(this.plannerApiUrl + '/degreePlan/' + planId + '/notes/' + noteId, httpOptions)
+		.pipe(catchError(this.errorHandler));
+	}
+
+	saveFavoriteCourse(subjectCode: string, courseId: string): Observable<SavedForLaterCourse> {
+		return this.http.post<SavedForLaterCourse>(this.plannerApiUrl + '/favorites/' + subjectCode + '/' + courseId, httpOptions)
+		.pipe(catchError(this.errorHandler));
+	}
+
+	removeFavoriteCourse(subjectCode, courseId): Observable<SavedForLaterCourse> {
+		return this.http.delete<SavedForLaterCourse>(this.plannerApiUrl + '/favorites/' + subjectCode + '/' + courseId, httpOptions)
+		.pipe(catchError(this.errorHandler));
+	}
+
+	autocomplete(search: string) {
+		const data = {
+			subjectCode: '104',
+			termCode: '0000',
+			matchText: search
+		};
+
+		return this.http.post('/api/search/v1/autocomplete', data, httpOptions)
+		.pipe(catchError(this.errorHandler));
+	}
+
+	addCourse(planId, subjectCode, courseId, termCode) {
+		return this.http.post(this.plannerApiUrl + '/degreePlan/' + planId + '/courses', {subjectCode, courseId, termCode }, httpOptions)
+		.pipe(catchError(this.errorHandler));
+	}
+
+	removeCourse(planId, recordId) {
+		return this.http.delete(this.plannerApiUrl + '/degreePlan/' + planId + '/courses/' + recordId, httpOptions)
+		.pipe(catchError(this.errorHandler));
+	}
+
+	updateCourseTerm(planId, recordId, termCode): Observable<Course> {
+		return this.http.put<Course>(this.plannerApiUrl + '/degreePlan/' + planId + '/courses/' + recordId + '?termCode=' + termCode, httpOptions)
+		.pipe(catchError(this.errorHandler));
+	}
+
+test() {
+// return this.http.delete(this.plannerApiUrl + '/degreePlan/519260/courses/259445', httpOptions)
+return this.http.put(this.plannerApiUrl + '/degreePlan/519260/courses/259465?termCode=1174', httpOptions)
+	.pipe(catchError(this.errorHandler));
+}
+
+	private errorHandler(error: HttpErrorResponse) {
+		return throwError(error || 'Server Error');
+	}
+}
diff --git a/src/app/degree-planner/degree-planner.module.ts b/src/app/degree-planner/degree-planner.module.ts
index cd9a4a4..12364b4 100644
--- a/src/app/degree-planner/degree-planner.module.ts
+++ b/src/app/degree-planner/degree-planner.module.ts
@@ -13,22 +13,21 @@ import { DragDropModule } from '@angular/cdk/drag-drop';
 import { RemoveCourseConfirmDialogComponent } from './dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component';
 
 @NgModule({
-  imports: [SharedModule, DragDropModule, DegreePlannerRoutingModule],
-  exports: [DragDropModule],
-  declarations: [
-    DegreePlannerComponent,
-    TermContainerComponent,
-    CourseItemComponent,
-    SidenavMenuItemComponent,
-    SavedForLaterContainerComponent,
-    CourseDetailsDialogComponent,
-    NotesDialogComponent,
-    RemoveCourseConfirmDialogComponent,
-  ],
-  entryComponents: [
-    CourseDetailsDialogComponent,
-    NotesDialogComponent,
-    RemoveCourseConfirmDialogComponent,
-  ],
+	imports: [SharedModule, DragDropModule, DegreePlannerRoutingModule],
+	exports: [DragDropModule],
+	declarations: [
+		DegreePlannerComponent,
+		TermContainerComponent,
+		CourseItemComponent,
+		SidenavMenuItemComponent,
+		SavedForLaterContainerComponent,
+		NotesDialogComponent,
+		RemoveCourseConfirmDialogComponent
+	],
+	entryComponents: [
+		CourseDetailsDialogComponent,
+		NotesDialogComponent,
+		RemoveCourseConfirmDialogComponent
+	]
 })
 export class DegreePlannerModule {}
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index 8d954b1..bb40482 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -20,6 +20,9 @@ import { GetTermDescriptionPipe } from './pipes/get-term-description.pipe';
 import { AcademicYearStatePipe } from './pipes/academic-year-state.pipe';
 import { AcademicYearRangePipe } from './pipes/academic-year-range.pipe';
 import { CourseDetailsComponent } from './components/course-details/course-details.component';
+import { MatAutocompleteModule } from '@angular/material/autocomplete';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { CourseDetailsDialogComponent } from '../degree-planner/dialogs/course-details-dialog/course-details-dialog.component';
 
 const modules = [
 	CommonModule,
@@ -37,7 +40,9 @@ const modules = [
 	MatToolbarModule,
 	MatDialogModule,
 	MatInputModule,
-	MatTooltipModule
+	MatTooltipModule,
+	MatAutocompleteModule,
+	MatFormFieldModule
 ];
 const pipes = [
 	GetTermDescriptionPipe, AcademicYearStatePipe, AcademicYearRangePipe
@@ -46,7 +51,7 @@ const pipes = [
 @NgModule({
 	imports: [ modules ],
 	exports: [ modules, pipes, CourseDetailsComponent ],
-	declarations: [ pipes, CourseDetailsComponent ]
+	declarations: [ pipes, CourseDetailsComponent, CourseDetailsDialogComponent ]
 })
 
 export class SharedModule { }
-- 
GitLab