Forked from an inaccessible project.
-
Isaac Evavold authoredIsaac Evavold authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
reducer.ts 7.25 KiB
import {
DegreePlannerState,
INITIAL_DEGREE_PLANNER_STATE,
} from '@app/degree-planner/store/state';
import {
PlanActionTypes,
InitialPlanLoadResponse,
ChangeVisiblePlanResponse,
} from '@app/degree-planner/store/actions/plan.actions';
import {
CourseActionTypes,
RemoveCourseResponse,
ChangeCourseTermResponse,
AddCourseResponse,
RemoveSavedForLaterResponse,
AddSavedForLaterResponse,
} from '@app/degree-planner/store/actions/course.actions';
import {
NoteActionTypes,
WriteNoteResponse,
DeleteNoteResponse,
} from '@app/degree-planner/store/actions/note.actions';
import { SavedForLaterCourse } from '@app/core/models/saved-for-later-course';
type SupportedActions =
| InitialPlanLoadResponse
| ChangeVisiblePlanResponse
| WriteNoteResponse
| DeleteNoteResponse
| ChangeCourseTermResponse
| RemoveCourseResponse
| AddCourseResponse
| RemoveSavedForLaterResponse
| AddSavedForLaterResponse;
export function degreePlannerReducer(
state = INITIAL_DEGREE_PLANNER_STATE,
action: SupportedActions,
): DegreePlannerState {
switch (action.type) {
/**
* The `InitialPlanLoadResponse` action is triggered on initial Degree
* Planner app load. It downloads a list of the user's degree plans and
* picks the primary plan from that list to load as the first visible plan.
*/
case PlanActionTypes.InitialPlanLoadResponse: {
return { ...action.payload };
}
/**
* The `ChangeVisiblePlanResponse` action is triggered whenever the UI needs
* to switch which degree plan is being shown and load the data associated
* with that degree plan. The reducer extracts that downloaded data from the
* action payload and builds a new state using that data.
*/
case PlanActionTypes.ChangeVisiblePlanResponse: {
return { ...state, ...action.payload };
}
/**
* The `WriteNoteResponse` action is dispatched by the `Note.write$` effect
* upon a successful response from the `updateNote` or `createNote` API
* endpoints. The reducer in this case either:
*
* - Replaces a note on a term that already had a note.
* - *OR* adds a note to a term that didn't previously have a note.
* - *OR* adds a new term with the given note if no term exists with the note's termCode.
*/
case NoteActionTypes.WriteNoteResponse: {
const updatedNote = action.payload.updatedNote;
const updatedTermCode = updatedNote.termCode;
const originalTerms = state.visibleTerms;
if (termCodeExists(updatedTermCode, originalTerms)) {
/**
* If a term with the given `termCode` *DOES exist* in the state,
* replace just that term with the new data inside the action.
*/
const newVisibleTerms = originalTerms.map(term => {
if (term.termCode === updatedTermCode) {
return { ...term, note: updatedNote };
} else {
return term;
}
});
return { ...state, visibleTerms: newVisibleTerms };
} else {
/**
* If a term with the given `termCode` *DOES NOT exist*
* in the state, add it to the end of the term list.
*/
const newVisibleTerms = originalTerms.concat({
termCode: updatedTermCode,
note: updatedNote,
courses: [],
});
return { ...state, visibleTerms: newVisibleTerms };
}
}
/**
* The `DeleteNoteResponse` action is dispatched after the `deleteNote` API
* has been called and it is okay to remote the note with the given
* termCode from the degree planner state.
*/
case NoteActionTypes.DeleteNoteResponse: {
const deletedTermCode = action.payload.termCode;
const originalTerms = state.visibleTerms;
if (termCodeExists(deletedTermCode, originalTerms)) {
/**
* If a term with the given `termCode` *DOES EXIST* in the state,
* remove that term's note.
*/
const newVisibleTerms = originalTerms.map(term => {
if (term.termCode === deletedTermCode) {
return { ...term, note: undefined };
} else {
return term;
}
});
return { ...state, visibleTerms: newVisibleTerms };
} else {
return state;
}
}
case CourseActionTypes.ChangeCourseTermResponse: {
const { to, from, id } = action.payload;
const t = state.visibleTerms.find(term => term.termCode === from);
if (t) {
const course = t.courses.find(c => c.id === id);
if (course) {
course.termCode = to;
// Create new visibleTerms array
const newVisibleTerms = state.visibleTerms.map(term => {
if (term.termCode === from) {
// Remove the course from the previous term
term.courses = term.courses.filter(c => c.id !== id);
} else if (term.termCode === to) {
// Add the new course to this term
term.courses = [...term.courses, course];
}
return term;
});
return { ...state, visibleTerms: newVisibleTerms };
}
return state;
}
return state;
}
case CourseActionTypes.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 CourseActionTypes.RemoveCourseResponse: {
const { recordId: 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 };
}
case CourseActionTypes.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 CourseActionTypes.AddSavedForLaterResponse: {
const newSavedForLater: SavedForLaterCourse[] = [
...state.savedForLaterCourses,
{
id: null,
courseId: action.payload.courseId,
termCode: '0000',
topicId: 0,
subjectCode: action.payload.subjectCode,
title: action.payload.title,
catalogNumber: action.payload.catalogNumber,
courseOrder: 0,
subject: state.subjects[action.payload.subjectCode],
},
];
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.
*/
default:
return state;
}
}
const termCodeExists = (termCode: string, things: { termCode: string }[]) => {
return things.some(thing => thing.termCode === termCode);
};