diff --git a/src/app/core/actions.ts b/src/app/core/actions.ts index 907f1ab058c6e3d13484d7924c5ba5f3f0945622..0b224cbe3a30cce7845bb85a2b13561fb146e387 100644 --- a/src/app/core/actions.ts +++ b/src/app/core/actions.ts @@ -5,7 +5,16 @@ export enum UserPreferencesActionTypes { UpdateUserPreferences = '[User Preferences] Update', } +export enum NotificationActionTypes { + DismissAlert = '[UI] Dismiss alert', +} + export class UpdateUserPreferences implements Action { public readonly type = UserPreferencesActionTypes.UpdateUserPreferences; constructor(public payload: Partial<UserPreferences>) {} } + +export class DismissAlert implements Action { + public readonly type = NotificationActionTypes.DismissAlert; + constructor(public payload: { key: string }) {} +} diff --git a/src/app/core/models/alert.ts b/src/app/core/models/alert.ts index 1d78964a32445a05d184c931ac79dedcac4f5fe8..ad6cfe479f4dc596ca5520d09c30650b2e2bd791 100644 --- a/src/app/core/models/alert.ts +++ b/src/app/core/models/alert.ts @@ -13,3 +13,12 @@ export class DisclaimerAlert implements Alert { constructor(public callback: Alert['callback']) {} } + +export class DarsDisclaimerAlert implements Alert { + public readonly key = 'disclaimerAlert'; + public readonly title = 'This is a planning tool Dars.'; + public readonly message = + 'If you have questions about your plan or degree, please contact your advisor.'; + + constructor(public callback: Alert['callback']) {} +} diff --git a/src/app/core/models/user-preferences.ts b/src/app/core/models/user-preferences.ts index cc01a77b6e9122fc6fa2af1b0b671061405e99d4..a2017138c43757508eb8aa14ce17f06f2b17ffd7 100644 --- a/src/app/core/models/user-preferences.ts +++ b/src/app/core/models/user-preferences.ts @@ -3,4 +3,5 @@ export interface UserPreferences { degreePlannerSelectedPlan?: number; degreePlannerHasDismissedDisclaimer?: boolean; degreePlannerHasDismissedIEWarning?: boolean; + darsHasDismissedDisclaimer?: boolean; } diff --git a/src/app/dars/dars-view/dars-view.component.html b/src/app/dars/dars-view/dars-view.component.html index f296898ffe9b5229001eb10681e3a0023441c210..00ef9c9625a3032dfcfbba251e2e56ad4320846d 100644 --- a/src/app/dars/dars-view/dars-view.component.html +++ b/src/app/dars/dars-view/dars-view.component.html @@ -20,6 +20,7 @@ <!-- Main DARS Content --> <div id="dars-main"> + <cse-alert-container></cse-alert-container> <h2 class="mat-h1">Completed Audit Requests</h2> <div id="dars-header-bar"> diff --git a/src/app/dars/dars.module.ts b/src/app/dars/dars.module.ts index 08b9305b6bc565949563b75ec42f4090dc32773a..1a2b72c267c1de5a0d64c01fe5e8ecbd9ea3c8a6 100644 --- a/src/app/dars/dars.module.ts +++ b/src/app/dars/dars.module.ts @@ -7,6 +7,7 @@ import { DarsAuditComponent } from './audit/audit.component'; import { DarsMetadataTableComponent } from './metadata-table/metadata-table.component'; import { NewAuditOptionsComponent } from './dars-view/new-audit-options/new-audit-options.component'; import { MatStepperModule } from '@angular/material'; +import { AlertContainerComponent } from '../shared/components/alert-container/alert-container.component'; @NgModule({ imports: [ diff --git a/src/app/dars/store/actions.ts b/src/app/dars/store/actions.ts index 6124756085fab68a6ead1ea0387081404d0c1527..43d1a9c2ba5a89f4b97bf15d4219e6dc81000a7d 100644 --- a/src/app/dars/store/actions.ts +++ b/src/app/dars/store/actions.ts @@ -1,3 +1,4 @@ +import { DARSState } from '@app/dars/store/state'; import { Action } from '@ngrx/store'; import { AuditMetadata } from '../models/audit-metadata'; import { Audit } from '../models/audit'; @@ -5,6 +6,7 @@ import { Audit } from '../models/audit'; export enum DarsActionTypes { ErrorLoadingMetadata = '[DARS] Error Loading Metadata', StartLoadingMetadata = '[DARS] Start Loading Metadata', + DoneLoadingMetadata = '[DARS] Done Loading Metadata', ErrorLoadingAudit = '[DARS] Error Loading Audit', AddAuditMetadata = '[DARS] Add Audit Metadata', StartLoadingAudit = '[DARS] Start Loading Audit', @@ -21,6 +23,11 @@ export class StartLoadingMetadata implements Action { public readonly type = DarsActionTypes.StartLoadingMetadata; } +export class DoneLoadingMetadata implements Action { + public readonly type = DarsActionTypes.DoneLoadingMetadata; + constructor(public payload: DARSState) {} +} + export class ErrorLoadingAudit implements Action { public readonly type = DarsActionTypes.ErrorLoadingAudit; constructor(public payload: { message: string }) {} diff --git a/src/app/dars/store/effects.ts b/src/app/dars/store/effects.ts index 150fe2a6ed5a69218da29ca8e3c738c23532b2c5..ec73776b9a436c4cb0170e626336e111456b16e2 100644 --- a/src/app/dars/store/effects.ts +++ b/src/app/dars/store/effects.ts @@ -1,26 +1,73 @@ +import { visibleAudit } from './selectors'; +import { UpdateUserPreferences } from './../../core/actions'; +import { GlobalState } from '@app/core/state'; +import { Store } from '@ngrx/store'; +import { forkJoinWithKeys } from '@app/degree-planner/shared/utils'; import { Injectable } from '@angular/core'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { DarsActionTypes } from '@app/dars/store/actions'; import * as darsActions from '@app/dars/store/actions'; -import { flatMap, map, catchError } from 'rxjs/operators'; +import { flatMap, map, catchError, switchMap } from 'rxjs/operators'; import { DarsApiService } from '../services/api.service'; import { of } from 'rxjs'; +import { Alert, DarsDisclaimerAlert } from '@app/core/models/alert'; +import { DegreePlannerApiService } from '@app/degree-planner/services/api.service'; +import { INITIAL_DARS_STATE } from './state'; @Injectable() export class DARSEffects { - constructor(private actions$: Actions, private api: DarsApiService) {} + constructor( + private actions$: Actions, + private api: DarsApiService, + private degreeAPI: DegreePlannerApiService, + private store$: Store<GlobalState>, + ) {} @Effect() load$ = this.actions$.pipe( ofType(DarsActionTypes.StartLoadingMetadata), - flatMap(() => this.api.getAudits()), - map(metadata => new darsActions.AddAuditMetadata({ metadata })), - catchError(() => { - return of( - new darsActions.ErrorLoadingMetadata({ - message: 'Unable to load audit metadata. Please try again', + + flatMap(() => { + return forkJoinWithKeys({ + metadata: this.api.getAudits(), + userPreferences: this.degreeAPI.getUserPreferences(), + }); + }), + + flatMap(({ metadata, userPreferences }) => { + const alerts: Alert[] = []; + if (userPreferences.darsHasDismissedDisclaimer !== true) { + alerts.push( + new DarsDisclaimerAlert(() => { + this.store$.dispatch( + new UpdateUserPreferences({ + darsHasDismissedDisclaimer: true, + }), + ); + }), + ); + } + + return forkJoinWithKeys({ + metadata: of(metadata), + visibleAudit: of({}), + alerts: of(alerts), + }).pipe( + switchMap(payload => { + return [ + new darsActions.AddAuditMetadata({ metadata }), + new UpdateUserPreferences(userPreferences), + ]; }), ); }), + + // catchError(() => { + // return of( + // new darsActions.ErrorLoadingMetadata({ + // message: 'Unable to load audit metadata. Please try again', + // }), + // ); + // }), ); } diff --git a/src/app/dars/store/state.ts b/src/app/dars/store/state.ts index 8986599668d48cb06443c516afa6d91489e44f93..c299147eed7987b4904ff8e7cbc21de9650551e3 100644 --- a/src/app/dars/store/state.ts +++ b/src/app/dars/store/state.ts @@ -1,3 +1,4 @@ +import { Alert } from './../../core/models/alert'; import { AuditMetadata } from '../models/audit-metadata'; import { Audit } from '../models/audit'; @@ -11,9 +12,11 @@ export interface DARSState { | { status: 'NotLoaded' } | { status: 'Loading'; metadata: AuditMetadata } | { status: 'Loaded'; metadata: AuditMetadata; audit: Audit }; + alerts: Alert[]; } export const INITIAL_DARS_STATE: DARSState = { metadata: { status: 'Loading' }, visibleAudit: { status: 'NotLoaded' }, + alerts: [], }; diff --git a/src/app/degree-planner/degree-planner.module.ts b/src/app/degree-planner/degree-planner.module.ts index a3d71e679ca8d940c0a5b5d1640c10703cdcfd1d..1a588798655e2c93a6b305a9a00c46d9e3e6fd46 100644 --- a/src/app/degree-planner/degree-planner.module.ts +++ b/src/app/degree-planner/degree-planner.module.ts @@ -10,7 +10,7 @@ import { DragDropModule } from '@angular/cdk/drag-drop'; import { RemoveCourseConfirmDialogComponent } from './dialogs/remove-course-confirm-dialog/remove-course-confirm-dialog.component'; import { YearContainerComponent } from '@app/degree-planner/year-container/year-container.component'; import { CourseSearchComponent } from '@app/degree-planner/course-search/course-search.component'; -import { AlertContainerComponent } from './alert-container/alert-container.component'; +import { AlertContainerComponent } from '../shared/components/alert-container/alert-container.component'; import { EffectsModule } from '@ngrx/effects'; import { DegreePlanEffects } from './store/effects/plan.effects'; import { NoteEffects } from './store/effects/note.effects'; @@ -38,7 +38,7 @@ import { ErrorEffects } from './store/effects/error.effects'; NotesDialogComponent, RemoveCourseConfirmDialogComponent, YearContainerComponent, - AlertContainerComponent, + // AlertContainerComponent, CourseSearchComponent, ], entryComponents: [NotesDialogComponent, RemoveCourseConfirmDialogComponent], diff --git a/src/app/degree-planner/store/actions/ui.actions.ts b/src/app/degree-planner/store/actions/ui.actions.ts index 06004588cfea197048bd7410b8be64b84d3a0dda..2fce691efcdaa3fc61120d1cb162a6dc9e960b26 100644 --- a/src/app/degree-planner/store/actions/ui.actions.ts +++ b/src/app/degree-planner/store/actions/ui.actions.ts @@ -16,7 +16,7 @@ export enum UIActionTypes { OpenSidenav = '[UI] Open Sidenav', CloseSidenav = '[UI] Close Sidenav', - DismissAlert = '[UI] Dismiss Disclaimer Alert', + // DismissAlert = '[UI] Dismiss Disclaimer Alert', UpdateUserPreferences = '[UI] Update User Preferences', @@ -66,10 +66,10 @@ export class CloseSidenav implements Action { public readonly type = UIActionTypes.CloseSidenav; } -export class DismissAlert implements Action { - public readonly type = UIActionTypes.DismissAlert; - constructor(public payload: { key: string }) {} -} +// export class DismissAlert implements Action { +// public readonly type = UIActionTypes.DismissAlert; +// constructor(public payload: { key: string }) {} +// } export class AddAcademicYear implements Action { public readonly type = UIActionTypes.AddAcademicYear; diff --git a/src/app/degree-planner/store/reducer.ts b/src/app/degree-planner/store/reducer.ts index 3b8f0f73d33fea40de0fcb1108834aebbf2f6960..1770f8fa31e3cc915c57502a3c98b155a0fb0c05 100644 --- a/src/app/degree-planner/store/reducer.ts +++ b/src/app/degree-planner/store/reducer.ts @@ -6,6 +6,7 @@ import * as planActions from '@app/degree-planner/store/actions/plan.actions'; import * as courseActions from '@app/degree-planner/store/actions/course.actions'; import * as noteActions from '@app/degree-planner/store/actions/note.actions'; import * as uiActions from '@app/degree-planner/store/actions/ui.actions'; +import * as globalActions from '@app/core/actions'; import { SavedForLaterCourse } from '@app/core/models/saved-for-later-course'; import { DegreePlan } from '@app/core/models/degree-plan'; import { Year, YearMapping } from '@app/core/models/year'; @@ -41,7 +42,7 @@ type SupportedActions = | planActions.DeletePlanSuccess | uiActions.ExpandAcademicYear | uiActions.CollapseAcademicYear - | uiActions.DismissAlert + | globalActions.DismissAlert | uiActions.OpenCourseSearch | uiActions.CloseCourseSearch | uiActions.ToggleCourseSearch @@ -143,7 +144,7 @@ export function degreePlannerReducer( return newState; } - case uiActions.UIActionTypes.DismissAlert: { + case globalActions.NotificationActionTypes.DismissAlert: { const keyToRemove = action.payload.key; const newAlerts = state.alerts.filter(({ key }) => key !== keyToRemove); return { ...state, alerts: newAlerts }; diff --git a/src/app/degree-planner/alert-container/alert-container.component.html b/src/app/shared/components/alert-container/alert-container.component.html similarity index 100% rename from src/app/degree-planner/alert-container/alert-container.component.html rename to src/app/shared/components/alert-container/alert-container.component.html diff --git a/src/app/degree-planner/alert-container/alert-container.component.scss b/src/app/shared/components/alert-container/alert-container.component.scss similarity index 100% rename from src/app/degree-planner/alert-container/alert-container.component.scss rename to src/app/shared/components/alert-container/alert-container.component.scss diff --git a/src/app/degree-planner/alert-container/alert-container.component.ts b/src/app/shared/components/alert-container/alert-container.component.ts similarity index 93% rename from src/app/degree-planner/alert-container/alert-container.component.ts rename to src/app/shared/components/alert-container/alert-container.component.ts index 8eabe02b84bffc8d8f6cdfff5426cf627d38975b..53ed03145bc516fd8fd4efce96395edb9370a4a7 100644 --- a/src/app/degree-planner/alert-container/alert-container.component.ts +++ b/src/app/shared/components/alert-container/alert-container.component.ts @@ -4,7 +4,7 @@ import { GlobalState } from '@app/core/state'; import * as selectors from '@app/degree-planner/store/selectors'; import { Alert } from '@app/core/models/alert'; import { Observable } from 'rxjs'; -import { DismissAlert } from '../store/actions/ui.actions'; +import { DismissAlert } from '@app/core/actions'; @Component({ selector: 'cse-alert-container', diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index aeb5f721294298ad963524486a53106f1fddff7d..b877a58480ba6e3677c6bef4c1c4f653dde37775 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -1,3 +1,4 @@ +import { AlertContainerComponent } from './components/alert-container/alert-container.component'; import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -85,7 +86,13 @@ const directives = [ClickStopPropagationDirective]; @NgModule({ imports: [modules], - exports: [modules, pipes, directives, CourseDetailsComponent], + exports: [ + modules, + pipes, + directives, + CourseDetailsComponent, + AlertContainerComponent, + ], entryComponents: [ConfirmDialogComponent, PromptDialogComponent], declarations: [ pipes, @@ -97,6 +104,7 @@ const directives = [ClickStopPropagationDirective]; IE11WarningDialogComponent, ConfirmDialogComponent, PromptDialogComponent, + AlertContainerComponent, ], }) export class SharedModule {} diff --git a/src/assets/mock-data/metadata-response.json b/src/assets/mock-data/metadata-response.json new file mode 100644 index 0000000000000000000000000000000000000000..9c595476e4a50d4db1f9c8790504ddfc7ecf259d --- /dev/null +++ b/src/assets/mock-data/metadata-response.json @@ -0,0 +1,17 @@ +[ + { + "darsJobId": "abc123", + "darsAuditRunDate": "2019-02-04T20:41:31Z", + "sisEmplId": "string", + "darsInstitutionCode": "L&S", + "darsInstitutionCodeDescription": "Letters & Science, College of", + "darsDegreeProgramCode": "BA 916", + "darsStatusOfDegreeAuditRequest": "new", + "darsHonorsOptionCode": "Z", + "darsHonorsOptionDescription": "Omit Major, Include Liberal Arts", + "darsDegreeAuditReportId": 0, + "darsCatalogYearTerm": "20121", + "whichEnrolledCoursesIncluded": "previous", + "degreePlannerPlanName": "string" + } +]