Skip to content
Snippets Groups Projects
Commit 550af400 authored by Scott Berg's avatar Scott Berg
Browse files

Add toggle for showing and hiding grades.

parent 23cc8eea
No related branches found
No related tags found
No related merge requests found
Showing
with 101 additions and 13 deletions
export interface UserPreferences {
degreePlannerGradesVisibility?: boolean;
degreePlannerSelectedPlan?: number;
}
......@@ -101,10 +101,22 @@
fxLayoutAlign="start stretch"
style="margin:24px 24px 72px 24px">
<div id="year-mask" *ngIf="(isLoadingPlan$ | async)"></div>
<div id="accordion-controls">
<button mat-button color="primary" (click)="toggleAllYears(true)" aria-label="Expand All Years">Expand All</button>
<span>|</span>
<button mat-button color="primary" (click)="toggleAllYears(false)" aria-label="Collapse All Years">Collapse All</button>
<div id="plan-controls">
<div>
<mat-slide-toggle
color="primary"
labelPosition="before"
[checked]="showGrades$ | async"
(change)="changeGradeVisibility($event)">
Show Grades
</mat-slide-toggle>
</div>
<div class="expand-collapse">
<button mat-button color="primary" (click)="toggleAllYears(true)" aria-label="Expand All Years">Expand All</button>
<span>|</span>
<button mat-button color="primary" (click)="toggleAllYears(false)" aria-label="Collapse All Years">Collapse All</button>
</div>
</div>
<mat-accordion multi="true">
<cse-year-container
......
......@@ -112,7 +112,12 @@ mat-sidenav {
display: none;
}
#accordion-controls {
text-align: right;
#plan-controls {
display: flex;
justify-content: space-between;
align-items: center;
}
.expand-collapse {
color: map-get($uw-primary, 500);
}
......@@ -8,7 +8,7 @@ import { OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { select } from '@ngrx/store';
import { Component } from '@angular/core';
import { MatSelectChange } from '@angular/material';
import { MatSelectChange, MatSlideToggleChange } from '@angular/material';
import { MatDialog } from '@angular/material';
import { Store } from '@ngrx/store';
import { MediaMatcher } from '@angular/cdk/layout';
......@@ -24,6 +24,7 @@ import {
CreatePlan,
ChangePlanName,
DeletePlan,
ChangeGradeVisibility,
} from '@app/degree-planner/store/actions/plan.actions';
import { PromptDialogComponent } from '@app/shared/dialogs/prompt-dialog/prompt-dialog.component';
import { ConfirmDialogComponent } from '@app/shared/dialogs/confirm-dialog/confirm-dialog.component';
......@@ -47,6 +48,7 @@ export class DegreePlannerComponent implements OnInit {
public termsByAcademicYear: Object;
public mobileView: MediaQueryList;
public coursesData$: any;
public showGrades$: Observable<boolean>;
public degreePlan$: Observable<DegreePlan | undefined>;
public allDegreePlans$: Observable<ReadonlyArray<DegreePlan>>;
public termsByYear$: Observable<ReadonlyArray<Year>>;
......@@ -73,6 +75,8 @@ export class DegreePlannerComponent implements OnInit {
filter(isntUndefined),
);
this.showGrades$ = this.store.pipe(select(selectors.selectGradeVisibility));
this.allDegreePlans$ = this.store.pipe(
select(selectors.selectAllDegreePlans),
);
......@@ -248,6 +252,10 @@ export class DegreePlannerComponent implements OnInit {
return termCodes;
}
public changeGradeVisibility(event: MatSlideToggleChange) {
this.store.dispatch(new ChangeGradeVisibility(event.checked));
}
public closeCourseSearch() {
this.store.dispatch(new CloseCourseSearch());
}
......
......@@ -120,7 +120,8 @@
</mat-menu>
</div>
<div *ngIf="disabled" fxLayout="row" fxLayoutAlign="end center">
<p attr.aria-label="grade {{ course.grade }}">{{ course.grade || '&nbsp;' }}</p>
<p *ngIf="!(showGrades$ | async)">{{ '&nbsp;' }}</p>
<p *ngIf="showGrades$ | async" attr.aria-label="grade {{ course.grade }}">{{ course.grade || '&nbsp;' }}</p>
</div>
<div fxLayout="row" fxLayoutAlign="end center">
<p *ngIf="type !== 'saved'"
......
......@@ -54,6 +54,7 @@ export class CourseItemComponent implements OnInit {
public plannedCourses: ReadonlyArray<Course>;
public toActiveTerm: boolean;
public mobileView: MediaQueryList;
public showGrades$: Observable<boolean>;
constructor(
private api: DegreePlannerApiService,
......@@ -93,6 +94,8 @@ export class CourseItemComponent implements OnInit {
} else {
this.status = 'Normal';
}
this.showGrades$ = this.store.pipe(select(selectors.selectGradeVisibility));
}
onMenuOpen() {
......
......@@ -25,6 +25,8 @@ export enum PlanActionTypes {
ChangePlanName = '[Plan] Change Plan Name',
ChangePlanNameSuccess = '[Plan] Change Plan Name (Success)',
ChangePlanNameFailure = '[Plan] CHange Plan Name (Failure)',
ChangeGradeVisibility = '[Plan] Change Grade Visibility',
}
export class InitialLoadSuccess implements Action {
......@@ -37,6 +39,11 @@ export class SwitchPlan implements Action {
constructor(public payload: { newVisibleRoadmapId: number }) {}
}
export class ChangeGradeVisibility implements Action {
public readonly type = PlanActionTypes.ChangeGradeVisibility;
constructor(public visibility: boolean) {}
}
export class SwitchPlanSuccess implements Action {
public readonly type = PlanActionTypes.SwitchPlanSuccess;
constructor(
......
......@@ -30,12 +30,14 @@ import {
CreatePlanSuccess,
DeletePlan,
DeletePlanSuccess,
ChangeGradeVisibility,
} from '@app/degree-planner/store/actions/plan.actions';
import * as utils from '@app/degree-planner/shared/utils';
import { DegreePlan } from '@app/core/models/degree-plan';
import { PlannedTerm, PlannedTermNote } from '@app/core/models/planned-term';
import { INITIAL_DEGREE_PLANNER_STATE } from '@app/degree-planner/store/state';
import { YearMapping, MutableYearMapping } from '@app/core/models/year';
import { UserPreferences } from '@app/core/models/user-preferences';
import { Note } from '@app/core/models/note';
import { CourseBase, Course } from '@app/core/models/course';
import { pickTermEra } from '@app/degree-planner/shared/utils';
......@@ -104,7 +106,13 @@ export class DegreePlanEffects {
});
}
const showGrades =
userPreferences.degreePlannerGradesVisibility !== undefined
? userPreferences.degreePlannerGradesVisibility
: true;
return forkJoinWithKeys({
showGrades: of(showGrades),
visibleDegreePlan: of(visibleDegreePlan),
visibleYears,
savedForLaterCourses,
......@@ -158,7 +166,9 @@ export class DegreePlanEffects {
this.snackBar.open(message, undefined, {});
// Get the users current preferences and update the selected roadmapId
this.updateSelectedPlan(state.payload.visibleDegreePlan.roadmapId);
this.setUserPreferences({
degreePlannerSelectedPlan: state.payload.visibleDegreePlan.roadmapId,
});
}),
catchError(error => {
return of(
......@@ -171,6 +181,27 @@ export class DegreePlanEffects {
}),
);
@Effect({ dispatch: false })
gradeVisibility$ = this.actions$.pipe(
ofType<ChangeGradeVisibility>(PlanActionTypes.ChangeGradeVisibility),
withLatestFrom(this.store$),
map(([change, state]) => {
this.setUserPreferences({
degreePlannerGradesVisibility: change.visibility,
});
return state;
}),
catchError(error => {
return of(
new PlanError({
message: 'Unable to change grade visibility',
duration: 2000,
error,
}),
);
}),
);
@Effect()
MakePlanPrimary$ = this.actions$.pipe(
ofType<MakePlanPrimary>(PlanActionTypes.MakePlanPrimary),
......@@ -251,7 +282,9 @@ export class DegreePlanEffects {
});
}),
map(({ newPlan, newYears }) => {
this.updateSelectedPlan(newPlan.roadmapId);
this.setUserPreferences({
degreePlannerSelectedPlan: newPlan.roadmapId,
});
return new CreatePlanSuccess({ newPlan, newYears });
}),
tap(() => {
......@@ -295,7 +328,7 @@ export class DegreePlanEffects {
}),
);
private updateSelectedPlan(roadmapId: number) {
private setUserPreferences(changes: UserPreferences) {
// Get the users current preferences and update the selected roadmapId
this.api
......@@ -305,7 +338,7 @@ export class DegreePlanEffects {
this.api
.updateUserPreferences({
...prefs,
degreePlannerSelectedPlan: roadmapId,
...changes,
})
.toPromise();
// We have to .toPromise this to actually fire the API call
......
......@@ -18,6 +18,7 @@ import {
CreatePlanSuccess,
DeletePlanSuccess,
PlanError,
ChangeGradeVisibility,
} from '@app/degree-planner/store/actions/plan.actions';
import {
MoveCourseInsideTerm,
......@@ -94,7 +95,8 @@ type SupportedActions =
| ToggleCourseSearch
| OpenSidenav
| CloseSidenav
| UpdateSearchTermCode;
| UpdateSearchTermCode
| ChangeGradeVisibility;
export function degreePlannerReducer(
state = INITIAL_DEGREE_PLANNER_STATE,
......@@ -606,6 +608,10 @@ export function degreePlannerReducer(
return { ...state, allDegreePlans };
}
case PlanActionTypes.ChangeGradeVisibility: {
return { ...state, showGrades: action.visibility };
}
/**
* It's okay if the action didn't match any of the cases above. If that's
* the case, just return the existing state object.
......
......@@ -59,6 +59,11 @@ export const selectVisibleTerm = createSelector(
},
);
export const selectGradeVisibility = createSelector(
getDegreePlannerState,
(state: DegreePlannerState) => state.showGrades,
);
export const isCourseSearchOpen = createSelector(
getDegreePlannerState,
(state: DegreePlannerState) => {
......
......@@ -16,6 +16,7 @@ export interface DegreePlannerState {
isLoadingPlan: boolean;
isSidenavOpen: 'defer' | boolean;
alerts: Alert[];
showGrades: boolean;
}
export const INITIAL_DEGREE_PLANNER_STATE: DegreePlannerState = {
......@@ -29,4 +30,5 @@ export const INITIAL_DEGREE_PLANNER_STATE: DegreePlannerState = {
isLoadingPlan: true,
isSidenavOpen: 'defer',
alerts: [],
showGrades: true,
};
......@@ -9,6 +9,7 @@ import { MatIconModule } from '@angular/material/icon';
import { MatTabsModule } from '@angular/material/tabs';
import { MatCardModule } from '@angular/material/card';
import { MatSelectModule } from '@angular/material/select';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatListModule } from '@angular/material/list';
......@@ -53,6 +54,7 @@ const modules = [
MatSelectModule,
FlexLayoutModule,
MatSidenavModule,
MatSlideToggleModule,
MatListModule,
MatToolbarModule,
MatDialogModule,
......
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