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>
<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
......@@ -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
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);
});
......@@ -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 ]
......
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';
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 {}
......@@ -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 { }
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