From 037b1936d1cac5ac6c40cda56805998111a8620a Mon Sep 17 00:00:00 2001
From: ievavold <ievavold@wisc.edu>
Date: Mon, 10 Jun 2019 15:23:13 -0500
Subject: [PATCH] make alert component generic

Previously, the alert component only worked with the degree planner view. Now the alert component can be used in any view that supplies a list of alerts.
---
 src/app/dars/dars-view/dars-view.component.html  |  5 ++++-
 src/app/dars/dars-view/dars-view.component.ts    |  9 ++++++++-
 src/app/dars/store/actions.ts                    |  7 +++++++
 src/app/dars/store/reducer.ts                    | 11 ++++++++++-
 src/app/dars/store/selectors.ts                  |  5 +++++
 .../degree-planner-view.component.html           |  5 ++++-
 .../degree-planner-view.component.ts             | 10 +++++++++-
 src/app/degree-planner/store/selectors.ts        |  6 ++----
 .../alert-container.component.html               |  2 +-
 .../alert-container/alert-container.component.ts | 16 ++++------------
 10 files changed, 54 insertions(+), 22 deletions(-)

diff --git a/src/app/dars/dars-view/dars-view.component.html b/src/app/dars/dars-view/dars-view.component.html
index dca2532..735a5fd 100644
--- a/src/app/dars/dars-view/dars-view.component.html
+++ b/src/app/dars/dars-view/dars-view.component.html
@@ -21,7 +21,10 @@
 
   <!-- Main DARS Content -->
   <div id="dars-main">
-    <cse-alert-container></cse-alert-container>
+    <cse-alert-container
+      [alerts]="alerts$ | async"
+      (dismiss)="onDismissAlert($event)">
+    </cse-alert-container>
     <h2 class="mat-h1">Completed Audit Requests</h2>
 
     <div id="dars-header-bar">
diff --git a/src/app/dars/dars-view/dars-view.component.ts b/src/app/dars/dars-view/dars-view.component.ts
index d3ea00e..74b1103 100644
--- a/src/app/dars/dars-view/dars-view.component.ts
+++ b/src/app/dars/dars-view/dars-view.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit, ViewChild } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { AuditMetadata } from '../models/audit-metadata';
 import { MatDialog } from '@angular/material';
 import { NewAuditOptionsComponent } from '../new-audit-options/new-audit-options.component';
@@ -10,6 +10,7 @@ import { Observable } from 'rxjs';
 import * as darsActions from '../store/actions';
 import { Audit } from '../models/audit';
 import { DarsApiService } from '../services/api.service';
+import { Alert } from '@app/core/models/alert';
 
 @Component({
   selector: 'cse-dars-view',
@@ -20,6 +21,7 @@ export class DARSViewComponent implements OnInit {
   public metadataStatus$: Observable<DARSState['metadata']['status']>;
   public visibleAuditStatus$: Observable<DARSState['visibleAudit']['status']>;
   public audit$: Observable<Audit | null>;
+  public alerts$: Observable<Alert[]>;
 
   constructor(
     private store: Store<GlobalState>,
@@ -33,6 +35,11 @@ export class DARSViewComponent implements OnInit {
     this.visibleAuditStatus$ = this.store.select(selectors.visibleAuditStatus);
     // this.audit$ = this.store.select(selectors.visibleAudit);
     this.audit$ = this.api.getAudit(3);
+    this.alerts$ = this.store.select(selectors.alerts);
+  }
+
+  public onDismissAlert(key: string) {
+    this.store.dispatch(new darsActions.DismissAlert({ key }));
   }
 
   public openNewAuditOptionsDialog() {
diff --git a/src/app/dars/store/actions.ts b/src/app/dars/store/actions.ts
index 83aba03..b7aed37 100644
--- a/src/app/dars/store/actions.ts
+++ b/src/app/dars/store/actions.ts
@@ -15,6 +15,8 @@ export enum DarsActionTypes {
   CloseAudit = '[DARS] Close Audit',
 
   PopulateDarsState = '[DARS] Done Loading state',
+
+  DismissAlert = '[DARS] Dismiss Alert',
 }
 
 export class ErrorLoadingMetadata implements Action {
@@ -64,3 +66,8 @@ export class DoneLoadingAudit implements Action {
 export class CloseAudit implements Action {
   public readonly type = DarsActionTypes.CloseAudit;
 }
+
+export class DismissAlert implements Action {
+  public readonly type = DarsActionTypes.DismissAlert;
+  constructor(public payload: { key: string }) {}
+}
diff --git a/src/app/dars/store/reducer.ts b/src/app/dars/store/reducer.ts
index 57cdf97..a64bef4 100644
--- a/src/app/dars/store/reducer.ts
+++ b/src/app/dars/store/reducer.ts
@@ -8,7 +8,8 @@ type SupportedActions =
   | darsActions.PopulateDarsState
   | darsActions.StartLoadingAudit
   | darsActions.DoneLoadingAudit
-  | darsActions.CloseAudit;
+  | darsActions.CloseAudit
+  | darsActions.DismissAlert;
 
 export function darsReducer(
   state = INITIAL_DARS_STATE,
@@ -72,6 +73,14 @@ export function darsReducer(
         visibleAudit: { status: 'NotLoaded' },
       };
     }
+    case DarsActionTypes.DismissAlert: {
+      return {
+        ...state,
+        alerts: state.alerts.filter(({ key }) => {
+          return key !== action.payload.key;
+        }),
+      };
+    }
     default:
       return state;
   }
diff --git a/src/app/dars/store/selectors.ts b/src/app/dars/store/selectors.ts
index a164fb0..37792d1 100644
--- a/src/app/dars/store/selectors.ts
+++ b/src/app/dars/store/selectors.ts
@@ -6,6 +6,11 @@ export const getDARSState = ({ dars }: GlobalState) => {
   return dars;
 };
 
+export const alerts = createSelector(
+  getDARSState,
+  (state: DARSState) => state.alerts,
+);
+
 export const metadataStatus = createSelector(
   getDARSState,
   (state: DARSState) => state.metadata.status,
diff --git a/src/app/degree-planner/degree-planner-view/degree-planner-view.component.html b/src/app/degree-planner/degree-planner-view/degree-planner-view.component.html
index e01a5cf..202cba5 100644
--- a/src/app/degree-planner/degree-planner-view/degree-planner-view.component.html
+++ b/src/app/degree-planner/degree-planner-view/degree-planner-view.component.html
@@ -9,7 +9,10 @@
         [ngClass]="{ isLoadingPlan: isLoadingPlan$ | async }"
         *ngIf="(degreePlan$ | async) as degreePlan">
 
-        <cse-alert-container></cse-alert-container>
+        <cse-alert-container
+          [alerts]="alerts$ | async"
+          (dismiss)="onDismissAlert($event)">
+        </cse-alert-container>
 
         <div
           id="maincontent"
diff --git a/src/app/degree-planner/degree-planner-view/degree-planner-view.component.ts b/src/app/degree-planner/degree-planner-view/degree-planner-view.component.ts
index e25a8e1..11f25f7 100644
--- a/src/app/degree-planner/degree-planner-view/degree-planner-view.component.ts
+++ b/src/app/degree-planner/degree-planner-view/degree-planner-view.component.ts
@@ -42,7 +42,8 @@ import { ConstantsService } from '../services/constants.service';
 import { TermCodeFactory } from '../services/termcode.factory';
 import { IE11WarningDialogComponent } from '../dialogs/ie11-warning-dialog/ie11-warning-dialog.component';
 import { getUserPreference } from '@app/core/selectors';
-import { UpdateUserPreferences } from '@app/core/actions';
+import { UpdateUserPreferences, DismissAlert } from '@app/core/actions';
+import { Alert } from '@app/core/models/alert';
 
 // From: https://stackoverflow.com/a/21825207
 const isIE11 =
@@ -67,6 +68,7 @@ export class DegreePlannerViewComponent implements OnInit {
   public isSidenavOpen$: Observable<boolean>;
   public hasDismissedIEWarning$: Observable<boolean>;
   public version: string;
+  public alerts$: Observable<Alert[]>;
 
   constructor(
     private store: Store<GlobalState>,
@@ -140,6 +142,12 @@ export class DegreePlannerViewComponent implements OnInit {
           });
       }
     });
+
+    this.alerts$ = this.store.select(selectors.alerts);
+  }
+
+  public onDismissAlert(key: string) {
+    this.store.dispatch(new DismissAlert({ key }));
   }
 
   public openSidenav() {
diff --git a/src/app/degree-planner/store/selectors.ts b/src/app/degree-planner/store/selectors.ts
index 677134e..1bc4729 100644
--- a/src/app/degree-planner/store/selectors.ts
+++ b/src/app/degree-planner/store/selectors.ts
@@ -90,11 +90,9 @@ export const getActiveSelectedSearchTerm = createSelector(
   },
 );
 
-export const getAlerts = createSelector(
+export const alerts = createSelector(
   getDegreePlannerState,
-  (state: DegreePlannerState) => {
-    return state.alerts;
-  },
+  (state: DegreePlannerState) => state.alerts,
 );
 
 export const isLoadingPlan = createSelector(
diff --git a/src/app/shared/components/alert-container/alert-container.component.html b/src/app/shared/components/alert-container/alert-container.component.html
index 242aca4..939d819 100644
--- a/src/app/shared/components/alert-container/alert-container.component.html
+++ b/src/app/shared/components/alert-container/alert-container.component.html
@@ -1,5 +1,5 @@
 <div class="cse-alert-container">
-  <div *ngFor="let alert of (alerts$ | async)" class="cse-alert">
+  <div *ngFor="let alert of alerts" class="cse-alert">
     <div class="alert-icon">
       <mat-icon>warning</mat-icon>
     </div>
diff --git a/src/app/shared/components/alert-container/alert-container.component.ts b/src/app/shared/components/alert-container/alert-container.component.ts
index 53ed031..0a1e1d6 100644
--- a/src/app/shared/components/alert-container/alert-container.component.ts
+++ b/src/app/shared/components/alert-container/alert-container.component.ts
@@ -1,10 +1,5 @@
-import { Component } from '@angular/core';
-import { Store, select } from '@ngrx/store';
-import { GlobalState } from '@app/core/state';
-import * as selectors from '@app/degree-planner/store/selectors';
+import { Component, Input, Output, EventEmitter } from '@angular/core';
 import { Alert } from '@app/core/models/alert';
-import { Observable } from 'rxjs';
-import { DismissAlert } from '@app/core/actions';
 
 @Component({
   selector: 'cse-alert-container',
@@ -12,14 +7,11 @@ import { DismissAlert } from '@app/core/actions';
   styleUrls: ['./alert-container.component.scss'],
 })
 export class AlertContainerComponent {
-  public alerts$: Observable<Alert[]>;
-
-  constructor(private store: Store<GlobalState>) {
-    this.alerts$ = store.pipe(select(selectors.getAlerts));
-  }
+  @Input() public alerts: Alert[];
+  @Output() public dismiss = new EventEmitter<string>();
 
   public dismissAlert(key: string, callback?: () => void) {
-    this.store.dispatch(new DismissAlert({ key }));
+    this.dismiss.emit(key);
     if (typeof callback === 'function') {
       callback();
     }
-- 
GitLab