diff --git a/src/app/core/data.service.ts b/src/app/core/data.service.ts index 5bb22f9fae40088c6b486c73cf18185a42da77a9..e5fbdf4fd1545bd79cc63c1b7d6e54989d6c447b 100644 --- a/src/app/core/data.service.ts +++ b/src/app/core/data.service.ts @@ -7,6 +7,7 @@ import { Course } from './models/course'; import { DegreePlan } from './models/degree-plan'; import { Term } from './models/term'; import { FavoriteCourse } from './models/favorite-course'; +import { CourseDetails } from './models/course-details'; const httpOptions = { headers: new HttpHeaders({ @@ -39,6 +40,11 @@ export class DataService { .pipe(catchError(this.errorHandler)); } + getCourseDetails(termCode: string, subjectCode: string, courseId: string): Observable<CourseDetails[]> { + return this.http.get<CourseDetails[]>(this.searchApiUrl + 'course/' + termCode + '/' + subjectCode + '/' + courseId, httpOptions) + .pipe(catchError(this.errorHandler)); + } + getFavoriteCourses(): Observable<FavoriteCourse[]> { return this.http.get<FavoriteCourse[]>(this.plannerApiUrl + '/favorites') .pipe(catchError(this.errorHandler)); diff --git a/src/app/core/models/course-details.ts b/src/app/core/models/course-details.ts new file mode 100644 index 0000000000000000000000000000000000000000..cbfb4e3c775154a8ff2ca9df21097dc52eba3766 --- /dev/null +++ b/src/app/core/models/course-details.ts @@ -0,0 +1,102 @@ + +export interface SchoolCollege { + academicOrgCode: string; + academicGroupCode: string; + shortDescription: string; + formalDescription: string; + uddsCode?: any; + schoolCollegeURI: string; +} + +export interface Subject { + termCode: string; + subjectCode: string; + description: string; + shortDescription: string; + formalDescription: string; + undergraduateCatalogURI: string; + graduateCatalogURI: string; + departmentURI: string; + uddsFundingSource: string; + schoolCollege: SchoolCollege; + footnotes: string[]; +} + +export interface Breadth { + code: string; + description: string; +} + +export interface LettersAndScienceCredits { + code: string; + description: string; +} + +export interface Level { + code: string; + description: string; +} + +export interface GradingBasis { + code: string; + description: string; +} + +export interface CourseRequirements { + "000008=": number[]; +} + +export interface Payload { + courseId: string; +} + +export interface TitleSuggest { + input: string[]; + payload: Payload; +} + +export interface CourseDetails { + termCode: string; + courseId: string; + subject: Subject; + catalogNumber: string; + approvedForTopics: boolean; + topics: any[]; + minimumCredits: number; + maximumCredits: number; + creditRange: string; + firstTaught: string; + lastTaught: string; + typicallyOffered: string; + generalEd?: any; + ethnicStudies?: any; + breadths: Breadth[]; + lettersAndScienceCredits: LettersAndScienceCredits; + workplaceExperience?: any; + foreignLanguage?: any; + honors?: any; + levels: Level[]; + openToFirstYear: boolean; + advisoryPrerequisites?: any; + enrollmentPrerequisites: string; + allCrossListedSubjects: any[]; + title: string; + description: string; + catalogPrintFlag: boolean; + academicGroupCode?: any; + currentlyTaught: boolean; + gradingBasis: GradingBasis; + repeatable: string; + gradCourseWork: boolean; + instructorProvidedContent?: any; + courseRequirements: CourseRequirements; + courseDesignation: string; + courseDesignationRaw: string; + fullCourseDesignation: string; + fullCourseDesignationRaw: string; + lastUpdated: number; + catalogSort: string; + subjectAggregate: string; + titleSuggest: TitleSuggest; +} + diff --git a/src/app/degree-planner/degree-planner.module.ts b/src/app/degree-planner/degree-planner.module.ts index 54cc17ecfd530d0df575914b0ce3fa53e757dcbe..abca80eac9782688640fe64df5507af166768fb5 100644 --- a/src/app/degree-planner/degree-planner.module.ts +++ b/src/app/degree-planner/degree-planner.module.ts @@ -2,11 +2,12 @@ import { NgModule } from '@angular/core'; import { DegreePlannerComponent } from './degree-planner.component'; import { SharedModule } from '@app/shared/shared.module'; -import {SharedDegreePlannerModule} from './shared/shared.module'; +import { SharedDegreePlannerModule } from './shared/shared.module'; import { DegreePlannerRoutingModule } from './degree-planner.routing.module'; import { TermContainerComponent } from './term-container/term-container.component'; import { SidenavMenuItemComponent } from './sidenav-menu-item/sidenav-menu-item.component'; import { FavoritesContainerComponent } from './favorites-container/favorites-container.component'; +import { CourseDetailsDialogComponent } from './dialogs/course-details-dialog/course-details-dialog.component'; @NgModule({ imports: [ @@ -19,6 +20,7 @@ import { FavoritesContainerComponent } from './favorites-container/favorites-con TermContainerComponent, SidenavMenuItemComponent, FavoritesContainerComponent - ] + ], + entryComponents: [ CourseDetailsDialogComponent ] }) export class DegreePlannerModule { } diff --git a/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.html b/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.html new file mode 100644 index 0000000000000000000000000000000000000000..4f740a0abf63582a5bc1810d303bac16f864893a --- /dev/null +++ b/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.html @@ -0,0 +1,70 @@ +<mat-toolbar color="primary" class="dialog-toolbar"> + <span class="dialog-toolbar-title">Course Details</span> + <button mat-button mat-dialog-close class="close-btn"><i class="material-icons">clear</i></button> +</mat-toolbar> + +<mat-dialog-content id="course-details-content" class="mat-typography dialog-with-toolbar "> + + <div fxLayout="row"> + <div fxLayout="row" fxFlex="100" fxLayoutGap="10px" class="course-details-header"> + <div fxFlex="50" class="course-detail-title" fxLayoutAlign="start center"> + <h3>{{ courseDetails.fullCourseDesignation | capitalize }}<span class="course-detail-subtitle">{{ courseDetails.title }}</span></h3> + </div> + <div fxFlex="50" fxLayout="row" fxLayoutAlign="end start" > + <mat-dialog-actions> + <button mat-button class="btn-secondary" mat-dialog-close>Delete</button> + <button mat-raised-button class="btn-primary mat-button">See Sections</button> + </mat-dialog-actions> + </div> + </div> + </div> + + <div fxLayout="column" fxLayoutAlign="space-around start" fxLayoutGap="10px" class="course-details-content"> + <p>{{ courseDetails.description }}</p> + <p *ngIf="courseDetails.advisoryPrerequisites && courseDetails.advisoryPrerequisites.length > 0"><span class="semi-bold">Requisites:</span>{{ courseDetails.advisoryPrerequisites }}</p> + + <ul> + <li *ngIf="courseDetails.creditRange"><span class="semi-bold">Credits:</span> {{ courseDetails.creditRange }}</li> + <li *ngIf="courseDetails.levels && courseDetails.levels.length > 0"><span class="semi-bold">Level:</span> + {{ courseDetails.levels | valuesToString:'description' }} + </li> + <li *ngIf="courseDetails.breadths && courseDetails.breadths.length > 0"><span class="semi-bold">Breadth:</span> + {{ courseDetails.breadths | valuesToString:'description' }} + </li> + <li *ngIf="courseDetails.lettersAndScienceCredits"><span class="semi-bold">L&S Credit Type:</span> + {{ courseDetails.lettersAndScienceCredits.description }} + </li> + </ul> + + <ul> + <li *ngIf="courseDetails.lastTaught"><span class="semi-bold">Last Taught:</span> {{ courseDetails.lastTaught | getTermDescription }}</li> + <li *ngIf="(courseDetails.gradCourseWork || courseDetails.workplaceExperience || courseDetails.foreignLanguage)" ><span class="semi-bold">Course Options:</span> + <ul> + <li> + <span *ngIf="courseDetails.gradCourseWork">50% Graduate Coursework Requirement</span> + <span *ngIf="courseDetails.workplaceExperience">{{ courseDetails.workplaceExperience.description }}</span> + <span *ngIf="courseDetails.foreignLanguage">{{ courseDetails.foreignLanguage.description }}</span> + </li> + </ul> + </li> + </ul> + + <p class="semi-bold">Subject Notes:</p> + <p>{{ courseDetails.subject.footnotes }}</p> + + </div> + + <div class="course-details-footer"> + <p class="semi-bold">{{ courseDetails.fullCourseDesignation | capitalize }} Information:</p> + <div fxLayout="row"> + <div fxLayout="row" fxFlex="100" fxLayoutGap="10px"> + <div fxFlex="100" class="course-detail-title" fxLayoutAlign="start center"> + <a class="md-primary btn-link mat-button" href="{{ courseDetails.subject.departmentURI }}">Website</a> + <a class="md-primary btn-link mat-button" href="{{ courseDetails.subject.undergraduateCatalogURI }}">Undergraduate Info</a> + <a class="md-primary btn-link mat-button" href="{{ courseDetails.subject.graduateCatalogURI }}">Graduate Info</a> + </div> + </div> + </div> + </div> + +</mat-dialog-content> \ No newline at end of file diff --git a/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.scss b/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..880b82955994329252465b7375011985dd157c15 --- /dev/null +++ b/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.scss @@ -0,0 +1,38 @@ +@import 'assets/material-theme.scss'; + +#course-details-content { + ul { + margin-bottom: 20px; + list-style: none; + padding-inline-start: 0px; + } + .course-detail-title { + h3 { + line-height: 1.2; + color: #333; + font-size: 16px; + font-weight: 500; + padding-top: 5px; + } + .course-detail-subtitle { + font-weight: 300; + display: block; + } + } + .mat-dialog-actions { + padding-top: 0px; + } + .course-details-header { + padding: 12px 0px; + } + .btn-link { + color: map-get($uw-primary, 500); + } + .course-details-footer { + margin-top: 5px; + .mat-dialog-actions { + margin-bottom: 0px; + padding-bottom: 0px; + } + } +} \ No newline at end of file diff --git a/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.spec.ts b/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0d75a5298460c9dcfccffe15f32b479d977c4eda --- /dev/null +++ b/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CourseDetailsDialogComponent } from './course-details-dialog.component'; + +describe('CourseDetailsDialogComponent', () => { + let component: CourseDetailsDialogComponent; + let fixture: ComponentFixture<CourseDetailsDialogComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CourseDetailsDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CourseDetailsDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.ts b/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..00ad9dc29e2486e66c6ed4df84343e3db34e3f59 --- /dev/null +++ b/src/app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component.ts @@ -0,0 +1,21 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { CourseDetails, Level, Breadth } from '../../../core/models/course-details'; +import { DataService } from '../../../core/data.service'; +import { MAT_DIALOG_DATA } from '@angular/material'; + +@Component({ + selector: 'cse-course-details-dialog', + templateUrl: './course-details-dialog.component.html', + styleUrls: ['./course-details-dialog.component.scss'] +}) + +export class CourseDetailsDialogComponent implements OnInit { + courseDetails: CourseDetails; + + constructor(@Inject(MAT_DIALOG_DATA) public data: any, private dataService: DataService) { + this.courseDetails = data.courseDetails; + } + + ngOnInit() { + } +} diff --git a/src/app/degree-planner/shared/shared.module.ts b/src/app/degree-planner/shared/shared.module.ts index adc9dc21cf52067c3c8f968999eef752ec9d8a49..a45d4de42d03b7e06a65b59b8f914b38491ce63e 100644 --- a/src/app/degree-planner/shared/shared.module.ts +++ b/src/app/degree-planner/shared/shared.module.ts @@ -2,11 +2,13 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CourseItemComponent } from './course-item/course-item.component'; import { DragDropModule } from '@angular/cdk/drag-drop'; +import { CourseDetailsDialogComponent } from '../dialogs/course-details-dialog/course-details-dialog.component'; +import { SharedModule } from '../../shared/shared.module'; @NgModule({ - imports: [ CommonModule, DragDropModule ], + imports: [ CommonModule, DragDropModule, SharedModule ], exports: [ CourseItemComponent, DragDropModule ], - declarations: [ CourseItemComponent ] + declarations: [ CourseItemComponent, CourseDetailsDialogComponent ] }) export class SharedDegreePlannerModule { } 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 6c4a113f614be0ef875f0893f1762cdda9b54a2b..8cfd400026610183d7c3883bd829fdf71b9600a2 100644 --- a/src/app/degree-planner/term-container/term-container.component.html +++ b/src/app/degree-planner/term-container/term-container.component.html @@ -11,7 +11,7 @@ (cdkDropListDropped)="drop($event)"> <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> + <cse-course-item [course]="course" [status]="'in-progress'" (click)="openCourseDetailsDialog(course)"></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.ts b/src/app/degree-planner/term-container/term-container.component.ts index a060c44f9c6f1e9bc0ea6e27a874af80c796889e..ccb71a647794b9f3b73d875b352dbe865c283edd 100644 --- a/src/app/degree-planner/term-container/term-container.component.ts +++ b/src/app/degree-planner/term-container/term-container.component.ts @@ -3,6 +3,9 @@ import { Term } from '../../core/models/term'; import { Course } from '../../core/models/course'; import { DataService } from '../../core/data.service'; import { CdkDragDrop, transferArrayItem } from '@angular/cdk/drag-drop'; +import { MatDialog } from '@angular/material'; +import { CourseDetailsDialogComponent } from '../dialogs/course-details-dialog/course-details-dialog.component'; +import { CourseDetails } from '../../core/models/course-details'; @Component({ selector: 'cse-term-container', @@ -15,8 +18,27 @@ export class TermContainerComponent { @Input() courses: Course[]; @Input() termCodes: String[]; @Input() termsByAcademicYear: Object; + @Input() course: CourseDetails; - constructor(private dataService: DataService) {} + terms: any[]; + favoriteCourse: FavoriteCourse; + + constructor(private dataService: DataService, public dialog: MatDialog) { + } + + openCourseDetailsDialog(course) { + this.dataService.getCourseDetails(course.termCode, course.subjectCode, course.courseId) + .subscribe(courseDetails => { + + const dialogRef = this.dialog.open(CourseDetailsDialogComponent, { + data: { courseDetails: courseDetails } + }); + + dialogRef.afterClosed().subscribe(result => { + console.log('Dialog was closed'); + }); + }); + } getTotalCredits() { if (!this.courses) { diff --git a/src/app/shared/capitalize.pipe.ts b/src/app/shared/capitalize.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..c001c8bf0a5ee7f0de633f9a1848f1c90d138e9a --- /dev/null +++ b/src/app/shared/capitalize.pipe.ts @@ -0,0 +1,15 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'capitalize' +}) +export class CapitalizePipe implements PipeTransform { + + transform(word: string, args?: any): any { + return word.toLowerCase() + .split(' ') + .map((char) => char.charAt(0).toUpperCase() + char.substring(1)) + .join(' '); + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 0abddb9487ab81f541d6363417547ac116ecb64d..f60ef0635560937757dc007ceb3c4aba3245fad0 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -11,9 +11,13 @@ import { MatCardModule } from '@angular/material/card'; import { MatSelectModule } from '@angular/material/select'; import { MatSidenavModule } from '@angular/material/sidenav'; import { MatExpansionModule } from '@angular/material/expansion'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatDialogModule } from '@angular/material/dialog'; import { GetTermDescriptionPipe } from './get-term-description.pipe'; import { AcademicYearStatePipe } from './academic-year-state.pipe'; +import { CapitalizePipe } from './capitalize.pipe'; +import { ValuesToStringPipe } from './values-to-string.pipe'; const modules = [ CommonModule, @@ -27,12 +31,15 @@ const modules = [ MatCardModule, MatSelectModule, FlexLayoutModule, - MatSidenavModule + MatSidenavModule, + MatToolbarModule, + MatDialogModule ]; @NgModule({ imports: [ modules ], - exports: [ modules, GetTermDescriptionPipe, AcademicYearStatePipe ], - declarations: [ GetTermDescriptionPipe, AcademicYearStatePipe ] + exports: [ modules, GetTermDescriptionPipe, AcademicYearStatePipe, CapitalizePipe, ValuesToStringPipe ], + declarations: [ GetTermDescriptionPipe, AcademicYearStatePipe, CapitalizePipe, ValuesToStringPipe ] }) + export class SharedModule { } diff --git a/src/app/shared/values-to-string.pipe.ts b/src/app/shared/values-to-string.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..9615a895c22b18a1cad880f5f2b1104c4cb0185b --- /dev/null +++ b/src/app/shared/values-to-string.pipe.ts @@ -0,0 +1,12 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'valuesToString' +}) + +export class ValuesToStringPipe implements PipeTransform { + + transform(array: [], key: any): any { + return array.map(element => element[key]).join(', '); + } +} diff --git a/src/assets/sass/general.scss b/src/assets/sass/general.scss index 8db0fb9c2da82e95197989c99a545c43761ef773..3a96171cd53122fb05462bbad031e5e8e7632d02 100644 --- a/src/assets/sass/general.scss +++ b/src/assets/sass/general.scss @@ -22,9 +22,16 @@ body { } .btn-primary { - background-color: map-get($uw-primary, 500); - color: #fff; - font-weight: 400; + background-color: #0479a8 !important; + color: white !important; + font-weight: 400 !important; + font-size: 14px; +} + +.btn-secondary { + color: map-get($uw-primary, 500) !important; + border: 1px solid #b7b7b7 !important; + border-radius: 4px !important; } .complete-icon{ @@ -109,4 +116,29 @@ body { .term-container-wrapper { display: flex !important; } -} \ No newline at end of file +} + +// Dialogs styles + +.mat-dialog-container { + min-width: 700px; + padding: 0px !important; + .dialog-with-toolbar { + padding: 0 45px 10px 45px; + } +} + +.dialog-toolbar { + height: 54px !important; + padding-right: 0px !important; + .dialog-toolbar-title { + flex: 1 1 auto; + font-weight: 400; + } + .close-btn { + width: 6px; + min-width: 60px; + padding: 1px 0px; + min-height: 60px; + } +}