diff --git a/src/app/degree-planner/actions/plan.actions.ts b/src/app/degree-planner/actions/plan.actions.ts index be3559d9e5ab023295ef540f6a5661d6bbd1508b..70182eb46bdcbb963c2058c79449404b3e6f5144 100644 --- a/src/app/degree-planner/actions/plan.actions.ts +++ b/src/app/degree-planner/actions/plan.actions.ts @@ -1,13 +1,20 @@ import { Action } from '@ngrx/store'; import { PlannedTerm } from '@app/core/models/planned-term'; import { DegreePlannerState } from '@app/degree-planner/state'; +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' + ChangeCourseTermResponse = '[Plan] Change Course Term Response', + MoveFromSavedToTermRequest = '[Plan] Move Course From Saved to Term Request', + MoveFromSavedToTermResponse= '[Plan] Move Course From Saved to Term Response' } export class InitialPlanLoadResponse implements Action { @@ -40,3 +47,31 @@ export class ChangeCourseTermResponse implements Action { public payload: {to: string, from: string, id: number} ) {} } + +export class AddCourseRequest implements Action { + public readonly type = PlanActionTypes.AddCourseRequest; + constructor( + public payload: {subjectCode: string, courseId: string, termCode: string} + ) {} +} + +export class AddCourseResponse implements Action { + public readonly type = PlanActionTypes.AddCourseResponse; + constructor( + public payload: {course: Course} + ) {} +} + +export class RemoveCourseRequest implements Action { + public readonly type = PlanActionTypes.RemoveCourseRequest; + constructor( + public payload: {id: number} + ) {} +} + +export class RemoveCourseResponse implements Action { + public readonly type = PlanActionTypes.RemoveCourseResponse; + constructor( + public payload: {id: number} + ) {} +} diff --git a/src/app/degree-planner/effects/plan.effects.ts b/src/app/degree-planner/effects/plan.effects.ts index a17defa55fadfb4c43b74aed63a9fa601c608034..b85d9a85bb73e3351264356a545e8919ea457812 100644 --- a/src/app/degree-planner/effects/plan.effects.ts +++ b/src/app/degree-planner/effects/plan.effects.ts @@ -8,7 +8,7 @@ import { Store } from '@ngrx/store'; // Services import { DegreePlannerApiService } from '@app/degree-planner/services/api.service'; -import { getDegreePlannerState } from '@app/degree-planner/selectors'; +import { getDegreePlannerState, getVisibleRoadmapId } from '@app/degree-planner/selectors'; // Actions import { @@ -16,7 +16,9 @@ import { ChangeVisiblePlanRequest, ChangeVisiblePlanResponse, PlanActionTypes, - ChangeCourseTermResponse + ChangeCourseTermResponse, + AddCourseResponse, + RemoveCourseResponse } from '@app/degree-planner/actions/plan.actions'; // Models @@ -111,6 +113,81 @@ export class DegreePlanEffects { }) ); + @Effect() + AddCourse$ = this.actions$.pipe( + ofType<any>(PlanActionTypes.AddCourseRequest), + + withLatestFrom(this.store$.select(getDegreePlannerState)), + filter(([_, state]) => typeof state.visibleRoadmapId === 'number'), + + // Get the roadmap ID from the action. + tap(([action, state]) => { + console.log(action); + console.log(state); + }), + + // Get term data for the degree plan specified by the roadmap ID. + flatMap(([action, state]) => { + // TODO error handle the API calls + const roadmapId = state.visibleRoadmapId; + const {subjectCode, termCode, courseId} = action.payload; + return this.api.addCourse(roadmapId as number, subjectCode, courseId, termCode).pipe( + map(response => { + return { + response, + action + }; + }) + ); + }), + + // 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}); + }) + ); + + @Effect() + RemoveCourse$ = this.actions$.pipe( + ofType<any>(PlanActionTypes.RemoveCourseRequest), + + withLatestFrom(this.store$.select(getDegreePlannerState)), + filter(([_, state]) => typeof state.visibleRoadmapId === 'number'), + + // Get the roadmap ID from the action. + tap(([action, state]) => { + console.log(action); + console.log(state); + }), + + // Get term data for the degree plan specified by the roadmap ID. + flatMap(([action, state]) => { + // TODO error handle the API calls + return this.api.removeCourse(state.visibleRoadmapId as number, action.payload.id).pipe( + map(response => { + return { + response, + action + }; + }) + ); + }), + + // Wrap data in an Action for dispatch + map(({ response, action }) => { + if (response === null) { + const { id } = action.payload; + return new RemoveCourseResponse({ id }); + } + return; + }) + ); + private loadTermsForPlan<T extends { visibleRoadmapId: number }>(stdin: T) { return forkJoin( this.api.getAllNotes(stdin.visibleRoadmapId), diff --git a/src/app/degree-planner/reducer.ts b/src/app/degree-planner/reducer.ts index c2c277a917f7f890f8010f0ac5021b6367dfeb87..aaacf1d6436df270826282fdbae98e487c120193 100644 --- a/src/app/degree-planner/reducer.ts +++ b/src/app/degree-planner/reducer.ts @@ -6,8 +6,9 @@ import { PlanActionTypes, InitialPlanLoadResponse, ChangeVisiblePlanResponse, - ChangeCourseTermRequest, - ChangeCourseTermResponse + RemoveCourseResponse, + ChangeCourseTermResponse, + AddCourseResponse } from '@app/degree-planner/actions/plan.actions'; import { NoteActionTypes, @@ -20,7 +21,9 @@ type SupportedActions = | ChangeVisiblePlanResponse | WriteNoteResponse | DeleteNoteResponse - | ChangeCourseTermResponse; + | ChangeCourseTermResponse + | RemoveCourseResponse + | AddCourseResponse; export function degreePlannerReducer( state = INITIAL_DEGREE_PLANNER_STATE, @@ -146,6 +149,36 @@ export function degreePlannerReducer( return state; } + case PlanActionTypes.AddCourseResponse: { + const { course } = action.payload; + + const newVisibleTerms = state.visibleTerms.map(term => { + if (term.termCode === course.termCode) { + term.courses.push(course); + } + return term; + }); + + return {...state, visibleTerms: newVisibleTerms}; + + // return {...state, visibleTerms: newVisibleTerms}; + // return state; + } + + case PlanActionTypes.RemoveCourseResponse: { + const { id } = action.payload; + + // Create new visibleTerms array + const newVisibleTerms = state.visibleTerms.map(term => { + term.courses = term.courses.filter(course => { + return course.id !== id; + }); + return term; + }); + + return {...state, visibleTerms: newVisibleTerms}; + } + /** * 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 287120260b70089447067bda044e86d053002377..734bdb89faa1540be0f0db1b8f63bd4e5ebc7698 100644 --- a/src/app/degree-planner/services/api.service.ts +++ b/src/app/degree-planner/services/api.service.ts @@ -58,6 +58,15 @@ export class DegreePlannerApiService { roadmapId + '/courses/' + recordId + '?termCode=' + termCode, HTTP_OPTIONS); } + public addCourse(planId: number, subjectCode: string, courseId: string, termCode: string) { + return this.http.post<Course>( + this.config.apiPlannerUrl + '/degreePlan/' + planId + '/courses', {subjectCode, courseId, termCode }, HTTP_OPTIONS); + } + + public removeCourse(planId: number, recordId: string) { + return this.http.delete(this.config.apiPlannerUrl + '/degreePlan/' + planId + '/courses/' + recordId, HTTP_OPTIONS); + } + public createNote( planId: number, termCode: string,