Skip to content
Snippets Groups Projects
Commit a9ee3c4a authored by pnogal's avatar pnogal Committed by Paulina Nogal
Browse files

Include Typeahead api

parent de9d05ba
No related branches found
No related tags found
No related merge requests found
<header> <header>
<cse-header></cse-header> <cse-header></cse-header>
<cse-navigation></cse-navigation> <cse-navigation></cse-navigation>
</header> </header>
<main> <main>
<mat-sidenav-container class="example-container" hasBackdrop="false" style="height: 100vh;"> <mat-sidenav-container class="example-container" hasBackdrop="false" style="height: 100vh;">
<mat-sidenav mode="over" position="end" #rightAddCourse> <mat-sidenav mode="over" position="end" #rightAddCourse id="course-search-sidenav">
<mat-sidenav-container> <mat-toolbar color="primary" class="dialog-toolbar">
<mat-sidenav mode="side" position="start" #rightAddCourse2 class="course-details-pane"> <span class="dialog-toolbar-title">Course Search</span>
<mat-toolbar color="primary" class="dialog-toolbar"> <button mat-button class="close-btn" (click)="rightAddCourse.close();"><i class="material-icons">clear</i></button>
<button mat-button class="close-btn" (click)="rightAddCourse2.close()"><i class="material-icons">clear</i></button> </mat-toolbar>
<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>
<p><span class="semi-bold">Subject Notes:</span><br> <div [formGroup]='coursesForm' class="add-course-form" fxLayout="column" fxLayoutAlign="space-around none" style="padding: 12px 22px;">
<span class="subject-notes"></span></p> <mat-form-field>
</div> <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"> <mat-form-field>
<p class="semi-bold course-detail-title">English Information:</p> <input matInput placeholder="Subject" [matAutocomplete]="auto" formControlName='coursesInput' required>
<div fxLayout="row"> <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
<div fxLayout="row" fxFlex="100" fxLayoutGap="10px"> <mat-option *ngFor="let course of (courses | async)" [value]="course.textSuggest">
<div fxFlex="100" fxLayout.lt-sm="column" class="mat-dialog-actions" fxLayoutAlign="start center" fxLayoutAlign.lt-sm="start start"> <span>{{ course.textSuggest }} </span>
<a class="md-primary btn-link mat-button" href="">Website</a> </mat-option>
<a class="md-primary btn-link mat-button" href="">Undergraduate Info</a> </mat-autocomplete>
<a class="md-primary btn-link mat-button" href="">Graduate Info</a> <mat-error *ngIf="coursesInput.invalid">Please select an existing Subject or 'All'.</mat-error>
</div> </mat-form-field>
</div> <mat-form-field class="example-full-width">
</div> <input matInput placeholder="Keyword, number" value="">
</div> </mat-form-field>
</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>
<div id="course-search-results" fxLayout="column" fxLayoutAlign="space-around none" style="padding: 12px 22px;"> <div class="search-results-toolbar mat-typography" fxLayout="row" fxLayoutAlign="space-between center" style="padding: 12px 22px; background-color: #EDF1F3">
<mat-form-field> <h3 style="margin: 0px;">15 results</h3>
<mat-select placeholder="Order by"> <span class="mat-button">Reset Search</span>
<mat-option>Relevance</mat-option> </div>
<mat-option>Subject</mat-option>
<mat-option>Catalog Number</mat-option>
</mat-select>
</mat-form-field>
<cse-course-item (click)="rightAddCourse2.toggle()"> <div id="course-search-results" fxLayout="column" fxLayoutAlign="space-around none" style="padding: 12px 22px;">
<div class="course-item"> <mat-form-field>
<p class="course-number">ENGL 162</p> <mat-select placeholder="Order by">
<p class="course-title">Shakespeare</p> <mat-option>Relevance</mat-option>
</div> <mat-option>Subject</mat-option>
<div class="course-item"> <mat-option>Catalog Number</mat-option>
<p class="course-number">PHILOS 101</p> </mat-select>
<p class="course-title">Introduction to Philosophy</p> </mat-form-field>
</div> <cse-course-item *ngFor="let course of (courses | async)" (click)="openCourseDetailsDialog(course)">
<div class="course-item"> <div class="course-item">
<p class="course-number">Math 135</p> <p class="course-number">{{ course.payload.subject.shortDescription }} {{ course.payload.catalogNumber }}</p>
<p class="course-title">Algebraic Reasoning for Teaching Math</p> <p class="course-title">{{ course.textSuggest }}</p>
</div>
</cse-course-item>
</div> </div>
</mat-sidenav-content> </cse-course-item>
</mat-sidenav-container> </div>
</mat-sidenav> </div>
</mat-sidenav>
<mat-sidenav-content> <mat-sidenav-content>
<router-outlet></router-outlet> <router-outlet></router-outlet>
</mat-sidenav-content> </mat-sidenav-content>
</mat-sidenav-container> </mat-sidenav-container>
</main> </main>
\ No newline at end of file
...@@ -20,13 +20,8 @@ main { ...@@ -20,13 +20,8 @@ main {
overflow: hidden; overflow: hidden;
} }
#course-details-content { #course-search-sidenav {
padding: 0 25px 25px 25px;
}
.course-search-pane,
.course-details-pane {
max-width: 360px; max-width: 360px;
min-width: 360px; min-width: 360px;
height: 100vh; height: 100vh;
} }
\ No newline at end of file
import { SidenavService } from './core/service/sidenav.service';
import { DataService } from './core/data.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 { Component, ViewChild, OnInit } from '@angular/core';
import { MatSidenav } from '@angular/material'; import { MatSidenav } from '@angular/material';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { DegreePlannerDataService } from './core/service/degree-planner-data.service'; 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({ @Component({
selector: 'cse-root', selector: 'cse-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'], styleUrls: ['./app.component.scss']
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
@ViewChild('rightAddCourse') public rightAddCourse: MatSidenav; coursesData$: any;
constructor(private sidenavService: SidenavService) {} selectedDegreePlan: number;
courses: Observable<any>;
coursesForm: FormGroup;
coursesInput: any;
ngOnInit() { @ViewChild('rightAddCourse') public rightAddCourse: MatSidenav;
this.sidenavService.setSidenav(this.rightAddCourse); constructor(
} public dialog: MatDialog,
coursesData$: any; private dataService: DataService,
selectedDegreePlan: number; 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; ngOnInit() {
constructor( this.sidenavService.setSidenav(this.rightAddCourse);
private dataService: DataService, this.coursesForm = this.fb.group({
private route: ActivatedRoute, coursesInput: null
private sidenavService: SidenavService, });
private degreePlannerDataSvc: DegreePlannerDataService) { this.courses = this.coursesForm.get('coursesInput').valueChanges
this.selectedDegreePlan = 520224; .pipe(
this.coursesData$ = this.degreePlannerDataSvc.getDegreePlanDataById(this.selectedDegreePlan); debounceTime(300),
} switchMap(value => this.dataService.autocomplete(value)),
tap(x => console.log( x))
);
}
ngOnInit() { openCourseDetailsDialog(course) {
this.sidenavService.setSidenav(this.rightAddCourse); 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() { document.addEventListener('WebComponentsReady', function() {
const customEvent = new CustomEvent('myuw-login', { const customEvent = new CustomEvent('myuw-login', {
detail: { detail: {
person: { person: {
// 'firstName': 'Bucky' // 'firstName': 'Bucky'
}, }
}, }
}); });
document.dispatchEvent(customEvent); document.dispatchEvent(customEvent);
}); });
...@@ -16,6 +16,7 @@ import { degreePlannerReducer } from '@app/degree-planner/reducer'; ...@@ -16,6 +16,7 @@ import { degreePlannerReducer } from '@app/degree-planner/reducer';
import { DegreePlanEffects } from '@app/degree-planner/effects/plan.effects'; import { DegreePlanEffects } from '@app/degree-planner/effects/plan.effects';
import { NoteEffects } from '@app/degree-planner/effects/note.effects'; import { NoteEffects } from '@app/degree-planner/effects/note.effects';
import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { CourseDetailsDialogComponent } from './degree-planner/dialogs/course-details-dialog/course-details-dialog.component';
@NgModule({ @NgModule({
imports: [ imports: [
...@@ -38,6 +39,7 @@ import { MatAutocompleteModule } from '@angular/material/autocomplete'; ...@@ -38,6 +39,7 @@ import { MatAutocompleteModule } from '@angular/material/autocomplete';
AppComponent, AppComponent,
HeaderComponent HeaderComponent
], ],
entryComponents: [CourseDetailsDialogComponent],
providers: [ SidenavService ], providers: [ SidenavService ],
bootstrap: [ AppComponent ], bootstrap: [ AppComponent ],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ] schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
......
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');
}
}
...@@ -13,22 +13,21 @@ import { DragDropModule } from '@angular/cdk/drag-drop'; ...@@ -13,22 +13,21 @@ import { DragDropModule } from '@angular/cdk/drag-drop';
import { RemoveCourseConfirmDialogComponent } from './dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component'; import { RemoveCourseConfirmDialogComponent } from './dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component';
@NgModule({ @NgModule({
imports: [SharedModule, DragDropModule, DegreePlannerRoutingModule], imports: [SharedModule, DragDropModule, DegreePlannerRoutingModule],
exports: [DragDropModule], exports: [DragDropModule],
declarations: [ declarations: [
DegreePlannerComponent, DegreePlannerComponent,
TermContainerComponent, TermContainerComponent,
CourseItemComponent, CourseItemComponent,
SidenavMenuItemComponent, SidenavMenuItemComponent,
SavedForLaterContainerComponent, SavedForLaterContainerComponent,
CourseDetailsDialogComponent, NotesDialogComponent,
NotesDialogComponent, RemoveCourseConfirmDialogComponent
RemoveCourseConfirmDialogComponent, ],
], entryComponents: [
entryComponents: [ CourseDetailsDialogComponent,
CourseDetailsDialogComponent, NotesDialogComponent,
NotesDialogComponent, RemoveCourseConfirmDialogComponent
RemoveCourseConfirmDialogComponent, ]
],
}) })
export class DegreePlannerModule {} export class DegreePlannerModule {}
...@@ -20,6 +20,9 @@ import { GetTermDescriptionPipe } from './pipes/get-term-description.pipe'; ...@@ -20,6 +20,9 @@ import { GetTermDescriptionPipe } from './pipes/get-term-description.pipe';
import { AcademicYearStatePipe } from './pipes/academic-year-state.pipe'; import { AcademicYearStatePipe } from './pipes/academic-year-state.pipe';
import { AcademicYearRangePipe } from './pipes/academic-year-range.pipe'; import { AcademicYearRangePipe } from './pipes/academic-year-range.pipe';
import { CourseDetailsComponent } from './components/course-details/course-details.component'; 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 = [ const modules = [
CommonModule, CommonModule,
...@@ -37,7 +40,9 @@ const modules = [ ...@@ -37,7 +40,9 @@ const modules = [
MatToolbarModule, MatToolbarModule,
MatDialogModule, MatDialogModule,
MatInputModule, MatInputModule,
MatTooltipModule MatTooltipModule,
MatAutocompleteModule,
MatFormFieldModule
]; ];
const pipes = [ const pipes = [
GetTermDescriptionPipe, AcademicYearStatePipe, AcademicYearRangePipe GetTermDescriptionPipe, AcademicYearStatePipe, AcademicYearRangePipe
...@@ -46,7 +51,7 @@ const pipes = [ ...@@ -46,7 +51,7 @@ const pipes = [
@NgModule({ @NgModule({
imports: [ modules ], imports: [ modules ],
exports: [ modules, pipes, CourseDetailsComponent ], exports: [ modules, pipes, CourseDetailsComponent ],
declarations: [ pipes, CourseDetailsComponent ] declarations: [ pipes, CourseDetailsComponent, CourseDetailsDialogComponent ]
}) })
export class SharedModule { } export class SharedModule { }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment