From e114c50b0b38c3f8cadec16f2377ef3b4ee226a9 Mon Sep 17 00:00:00 2001 From: Scott Berg <saberg3@wisc.edu> Date: Fri, 1 Feb 2019 15:28:33 -0600 Subject: [PATCH] Add actions / effects / reducers for adding and removing saved for later courses --- src/app/core/models/saved-for-later-course.ts | 2 +- .../degree-planner/actions/plan.actions.ts | 39 ++++++++ .../degree-planner/effects/plan.effects.ts | 97 +++++++++++++++++-- .../favorites-container.component.ts | 18 +++- src/app/degree-planner/reducer.ts | 41 +++++++- .../degree-planner/services/api.service.ts | 10 +- .../term-container.component.ts | 6 +- 7 files changed, 196 insertions(+), 17 deletions(-) diff --git a/src/app/core/models/saved-for-later-course.ts b/src/app/core/models/saved-for-later-course.ts index 2f95a9c..14d0113 100644 --- a/src/app/core/models/saved-for-later-course.ts +++ b/src/app/core/models/saved-for-later-course.ts @@ -1,5 +1,5 @@ export interface SavedForLaterCourse { - id: number; + id: number | null; courseId: string; termCode: string; topicId: number; diff --git a/src/app/degree-planner/actions/plan.actions.ts b/src/app/degree-planner/actions/plan.actions.ts index 70182eb..c716a6a 100644 --- a/src/app/degree-planner/actions/plan.actions.ts +++ b/src/app/degree-planner/actions/plan.actions.ts @@ -5,14 +5,25 @@ import { Course} from '../../core/models/course'; export enum PlanActionTypes { InitialPlanLoadResponse = '[Plan] Initial Load Response', + ChangeVisiblePlanRequest = '[Plan] Change Visible Request', ChangeVisiblePlanResponse = '[Plan] Change Visible Response', + AddCourseRequest = '[Plan] Add Course Request', AddCourseResponse = '[Plan] Add Course Response', + RemoveCourseRequest = '[Plan] Remove Course Request', RemoveCourseResponse = '[Plan] Remove Course Response', + ChangeCourseTermRequest = '[Plan] Change Course Term Request', ChangeCourseTermResponse = '[Plan] Change Course Term Response', + + AddSavedForLaterReqeust = '[Plan] Add Saved For Later Request', + AddSavedForLaterResponse = '[Plan] Add Saved For Later Response', + + RemoveSavedForLaterReqeust = '[Plan] Remove Saved For Later Request', + RemoveSavedForLaterResponse = '[Plan] Remove Saved For Later Response', + MoveFromSavedToTermRequest = '[Plan] Move Course From Saved to Term Request', MoveFromSavedToTermResponse= '[Plan] Move Course From Saved to Term Response' } @@ -75,3 +86,31 @@ export class RemoveCourseResponse implements Action { public payload: {id: number} ) {} } + +export class AddSavedForLaterRequest implements Action { + public readonly type = PlanActionTypes.AddSavedForLaterReqeust; + constructor( + public payload: {subjectCode: string, courseId: string} + ) {} +} + +export class AddSavedForLaterResponse implements Action { + public readonly type = PlanActionTypes.AddSavedForLaterResponse; + constructor( + public payload: {subjectCode: string, courseId: string} + ) {} +} + +export class RemoveSavedForLaterRequest implements Action { + public readonly type = PlanActionTypes.RemoveSavedForLaterReqeust; + constructor( + public payload: {subjectCode: string, courseId: string} + ) {} +} + +export class RemoveSavedForLaterResponse implements Action { + public readonly type = PlanActionTypes.RemoveSavedForLaterResponse; + constructor( + public payload: {subjectCode: string, courseId: string} + ) {} +} diff --git a/src/app/degree-planner/effects/plan.effects.ts b/src/app/degree-planner/effects/plan.effects.ts index b85d9a8..6775d84 100644 --- a/src/app/degree-planner/effects/plan.effects.ts +++ b/src/app/degree-planner/effects/plan.effects.ts @@ -18,7 +18,9 @@ import { PlanActionTypes, ChangeCourseTermResponse, AddCourseResponse, - RemoveCourseResponse + RemoveCourseResponse, + RemoveSavedForLaterResponse, + AddSavedForLaterResponse } from '@app/degree-planner/actions/plan.actions'; // Models @@ -144,10 +146,6 @@ export class DegreePlanEffects { // Wrap data in an Action for dispatch map(({ response, action }) => { // TODO add error handleing - console.log('------'); - console.log(action); - console.log(response); - console.log('------'); return new AddCourseResponse({ course: response}); }) ); @@ -160,10 +158,10 @@ export class DegreePlanEffects { filter(([_, state]) => typeof state.visibleRoadmapId === 'number'), // Get the roadmap ID from the action. - tap(([action, state]) => { - console.log(action); - console.log(state); - }), + // tap(([action, state]) => { + // console.log(action); + // console.log(state); + // }), // Get term data for the degree plan specified by the roadmap ID. flatMap(([action, state]) => { @@ -188,6 +186,87 @@ export class DegreePlanEffects { }) ); + @Effect() + RemoveSavedForLater$ = this.actions$.pipe( + ofType<any>(PlanActionTypes.RemoveSavedForLaterReqeust), + + withLatestFrom(this.store$.select(getDegreePlannerState)), + filter(([_, state]) => typeof state.visibleRoadmapId === 'number'), + + // tap(([action, state]) => { + // console.log('REMOVE SAVED FOR LATER ----------'); + + // console.log(action); + // console.log(state); + + // console.log('---------------------------------'); + // }), + + // Get term data for the degree plan specified by the roadmap ID. + flatMap(([action, state]) => { + // TODO error handle the API calls + return this.api.removeSavedForLater(action.payload.subjectCode, action.payload.courseId).pipe( + map(response => { + return { + response, + action + }; + }) + ); + }), + + // // Wrap data in an Action for dispatch + map(({ response, action }) => { + if (response === null) { + const { courseId, subjectCode } = action.payload; + return new RemoveSavedForLaterResponse({courseId, subjectCode}); + // TODO Update UI and remove saved response + } + return; + }) + ); + + @Effect() + SaveForLater$ = this.actions$.pipe( + ofType<any>(PlanActionTypes.AddSavedForLaterReqeust), + + withLatestFrom(this.store$.select(getDegreePlannerState)), + filter(([_, state]) => typeof state.visibleRoadmapId === 'number'), + + // Get the roadmap ID from the action. + // tap(([action, state]) => { + // console.log('ADD SAVED FOR LATER ----------'); + + // console.log(action); + // console.log(state); + + // console.log('---------------------------------'); + // }), + + // Get term data for the degree plan specified by the roadmap ID. + flatMap(([action, state]) => { + // TODO error handle the API calls + return this.api.saveForLater(action.payload.subjectCode, action.payload.courseId).pipe( + map(response => { + return { + response, + action + }; + }) + ); + }), + + // // // Wrap data in an Action for dispatch + map(({ response, action }) => { + if (response === null) { + const { subjectCode, courseId } = action.payload; + return new AddSavedForLaterResponse({ subjectCode, courseId }); + } + // return; + return; + }) + ); + private loadTermsForPlan<T extends { visibleRoadmapId: number }>(stdin: T) { return forkJoin( this.api.getAllNotes(stdin.visibleRoadmapId), diff --git a/src/app/degree-planner/favorites-container/favorites-container.component.ts b/src/app/degree-planner/favorites-container/favorites-container.component.ts index 2da8c38..3e82e55 100644 --- a/src/app/degree-planner/favorites-container/favorites-container.component.ts +++ b/src/app/degree-planner/favorites-container/favorites-container.component.ts @@ -7,6 +7,9 @@ import { getSavedForLaterCourses } from '@app/degree-planner/selectors'; // rsjx / ngrx import { DegreePlannerState } from '@app/degree-planner/state'; +import { + AddSavedForLaterRequest, RemoveCourseRequest +} from '@app/degree-planner/actions/plan.actions'; // Selectors import { @@ -30,6 +33,19 @@ export class SavedForLaterContainerComponent implements OnInit { } drop(event) { - console.log(event); + const newContainer = event.container.id; + const previousContainer = event.previousContainer.id; + + if (newContainer === previousContainer) { + // If the user dropped a course into the same container do nothing + return; + + } else if (previousContainer.indexOf('term-') === 0) { + // If moving from term to to saved for later + + const {subjectCode, courseId, id} = event.item.data; + this.store.dispatch(new AddSavedForLaterRequest({subjectCode, courseId})); + this.store.dispatch(new RemoveCourseRequest({id})); + } } } diff --git a/src/app/degree-planner/reducer.ts b/src/app/degree-planner/reducer.ts index aaacf1d..efd8643 100644 --- a/src/app/degree-planner/reducer.ts +++ b/src/app/degree-planner/reducer.ts @@ -8,7 +8,9 @@ import { ChangeVisiblePlanResponse, RemoveCourseResponse, ChangeCourseTermResponse, - AddCourseResponse + AddCourseResponse, + RemoveSavedForLaterResponse, + AddSavedForLaterResponse } from '@app/degree-planner/actions/plan.actions'; import { NoteActionTypes, @@ -23,7 +25,9 @@ type SupportedActions = | DeleteNoteResponse | ChangeCourseTermResponse | RemoveCourseResponse - | AddCourseResponse; + | AddCourseResponse + | RemoveSavedForLaterResponse + | AddSavedForLaterResponse; export function degreePlannerReducer( state = INITIAL_DEGREE_PLANNER_STATE, @@ -179,6 +183,39 @@ export function degreePlannerReducer( return {...state, visibleTerms: newVisibleTerms}; } + case PlanActionTypes.RemoveSavedForLaterResponse: { + const { courseId, subjectCode } = action.payload; + + // // Create new saved for later array + const newSavedForLater = state.savedForLaterCourses.filter( + course => course.subjectCode !== subjectCode && course.courseId !== courseId + ); + + return {...state, savedForLaterCourses: newSavedForLater}; + } + + case PlanActionTypes.AddSavedForLaterResponse: { + const { courseId, subjectCode } = action.payload; + + // // Create new saved for later array + const newSavedForLater = [ + ...state.savedForLaterCourses, + // TODO Update this when the API is fixed, the API should be sending a fav course as a response + { + id: null, + courseId, + subjectCode, + topicId: 0, + courseOrder: 0, + catalogNumber: '', + title: '', + termCode: '0000' + } + ]; + + return {...state, savedForLaterCourses: newSavedForLater}; + } + /** * 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. diff --git a/src/app/degree-planner/services/api.service.ts b/src/app/degree-planner/services/api.service.ts index 734bdb8..d71d7d6 100644 --- a/src/app/degree-planner/services/api.service.ts +++ b/src/app/degree-planner/services/api.service.ts @@ -58,7 +58,7 @@ export class DegreePlannerApiService { roadmapId + '/courses/' + recordId + '?termCode=' + termCode, HTTP_OPTIONS); } - public addCourse(planId: number, subjectCode: string, courseId: string, termCode: string) { + public addCourse(planId: number, subjectCode: string, courseId: string, termCode: string): Observable<Course> { return this.http.post<Course>( this.config.apiPlannerUrl + '/degreePlan/' + planId + '/courses', {subjectCode, courseId, termCode }, HTTP_OPTIONS); } @@ -67,6 +67,14 @@ export class DegreePlannerApiService { return this.http.delete(this.config.apiPlannerUrl + '/degreePlan/' + planId + '/courses/' + recordId, HTTP_OPTIONS); } + public saveForLater(subjectCode: string, courseId: string): Observable<SavedForLaterCourse> { + return this.http.post<SavedForLaterCourse>(this.config.apiPlannerUrl + '/favorites/' + subjectCode + '/' + courseId, HTTP_OPTIONS); + } + + public removeSavedForLater(subjectCode: string, courseId: string): Observable<SavedForLaterCourse> { + return this.http.delete<SavedForLaterCourse>(this.config.apiPlannerUrl + '/favorites/' + subjectCode + '/' + courseId, HTTP_OPTIONS); + } + public createNote( planId: number, termCode: string, 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 05f2897..42936df 100644 --- a/src/app/degree-planner/term-container/term-container.component.ts +++ b/src/app/degree-planner/term-container/term-container.component.ts @@ -9,7 +9,8 @@ import { Store, select } from '@ngrx/store'; import { DegreePlannerState } from '@app/degree-planner/state'; import { ChangeCourseTermRequest, - AddCourseRequest + AddCourseRequest, + RemoveSavedForLaterRequest } from '@app/degree-planner/actions/plan.actions'; // Selectors @@ -91,8 +92,6 @@ export class TermContainerComponent implements OnInit { } else if (previousContainer === 'saved-courses') { // If moving from saved courses to term - console.log('add course and remove from saved'); - console.log(event); // Get the term code from the new term dropzone's ID const termCode = newContainer.substr(5); @@ -102,6 +101,7 @@ export class TermContainerComponent implements OnInit { // Dispatch the add event this.store.dispatch(new AddCourseRequest({ subjectCode, courseId, termCode })); + this.store.dispatch(new RemoveSavedForLaterRequest({ subjectCode, courseId })); } } } -- GitLab