Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Custom Modules to Timetable #3373

Open
wants to merge 98 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
598e2ef
Add isCustom attribute
seanlowjk Dec 7, 2021
d820c36
Remove isCustomModule Filter
seanlowjk Dec 7, 2021
ae62c2d
Add Base UI for Custom Module Modal
seanlowjk Dec 8, 2021
ef034c7
Fix Module Preview and Styles
seanlowjk Dec 8, 2021
25149ff
Add Functionality for Fields
seanlowjk Dec 9, 2021
c7fd4dd
Integrate Custom Module Actions
seanlowjk Dec 9, 2021
e30c2b6
Integrate Reducers
seanlowjk Dec 9, 2021
0d43c75
Integrate Reducer Actions into components
seanlowjk Dec 9, 2021
0d0d5b3
Integrate Custom Modules into State
seanlowjk Dec 9, 2021
fc4b719
Update Colors for Custom Modules
seanlowjk Dec 9, 2021
96b4c32
Fix Dependency for CustomModuleSelect
seanlowjk Dec 9, 2021
b45a001
Fix CustomModuleSelect Logic
seanlowjk Dec 9, 2021
d261612
Integrate Custom Module Into Timetable
seanlowjk Dec 11, 2021
a639b52
Remove console.log statements
seanlowjk Dec 11, 2021
0db6822
Disable Module Page Links for Custom Modules
seanlowjk Dec 11, 2021
f833979
Fix Default State of Custom Module Select
seanlowjk Dec 11, 2021
d9146b3
Fix Styling for CustomModuleSelect
seanlowjk Dec 11, 2021
1bfe78a
Add REMOVE_CUSTOM_MODULE to UndoHistory
seanlowjk Dec 13, 2021
2486ace
Revert to Default State upon Custom Module Submission
seanlowjk Dec 13, 2021
4e08dab
Add Edit Functionality for Custom Modules
seanlowjk Dec 13, 2021
e932f4b
Fix State Alignment for Input Fields
seanlowjk Dec 13, 2021
e430ff6
Fix typos for renderWorkingDays
seanlowjk Dec 13, 2021
65f91f9
Abstract Modal Functionality into Common Component
seanlowjk Dec 15, 2021
64661e8
Fix Typo
seanlowjk Dec 15, 2021
e083305
Fix Modal Typos
seanlowjk Dec 15, 2021
217bc75
Fix Reducers when modifying module code
seanlowjk Dec 15, 2021
ab2c74b
Incorporate Custom Property into CustomModuleModal
seanlowjk Dec 15, 2021
c8fe406
Add Custom Utils for Custom Identifier
seanlowjk Dec 15, 2021
3c2d739
Abstract Custom Module Creation
seanlowjk Dec 15, 2021
e89f30e
Add tests for utils/custom.ts
seanlowjk Dec 15, 2021
56171ed
Add Data Validation
seanlowjk Dec 15, 2021
67e9c2b
Remove Unused form-control scss class
seanlowjk Dec 15, 2021
55390e1
Fix Linting
seanlowjk Dec 15, 2021
1d8df10
Fix Tests
seanlowjk Dec 15, 2021
2dbabe4
Fix CustomModuleData dependencies
seanlowjk Dec 15, 2021
be8e159
Fix TImetableModuleTable Test
seanlowjk Dec 15, 2021
0c88d3e
Remove Git Diff File
seanlowjk Dec 15, 2021
5012d7e
Standardize Types for Custom Modules for Timetable
seanlowjk Dec 15, 2021
7397281
Fix Action for MODIFY_CUSTOM_MODULE
seanlowjk Dec 15, 2021
c2d1476
Fix Actions in Components
seanlowjk Dec 15, 2021
8e92694
Fix Linting and Logic
seanlowjk Dec 15, 2021
1758ff9
Fix Tests
seanlowjk Dec 15, 2021
5a8c68f
Fix Reducer index
seanlowjk Dec 15, 2021
03e9467
Add ClassNo Field
seanlowjk Dec 15, 2021
be64ffe
Fix Minor Linting Issues
seanlowjk Dec 15, 2021
caea0fa
Fix Linting Errors
seanlowjk Dec 15, 2021
5b5f1e4
Remove trailing whitespaces
seanlowjk Dec 15, 2021
09839e0
Merge branch 'master' into feat/custom-modules
seanlowjk Dec 19, 2021
be8fc53
Merge branch 'master' into feat/custom-modules
seanlowjk Jan 13, 2022
a11c867
Merge branch 'master' into feat/custom-modules
seanlowjk Jan 16, 2022
b6fc08f
Add Tests for Actions and Reducers
seanlowjk Jan 16, 2022
d75a7d9
Merge branch 'master' into feat/custom-modules
seanlowjk Feb 11, 2022
f8ce7ea
Merge branch 'feat/custom-modules' of https://github.com/seanlowjk/nu…
jloh02 Aug 28, 2024
c512a16
Fix custom modules migration
jloh02 Aug 28, 2024
b1b6e28
Style dropdown for custom modules modal
jloh02 Aug 29, 2024
8e7db6b
Add weeks button group selector
jloh02 Aug 30, 2024
841ff55
Remove error box when validation passes
jloh02 Aug 30, 2024
289b1b1
Add shortcuts to update weeks
jloh02 Aug 30, 2024
8c8c3d4
Fix bug in odd even logic
jloh02 Aug 30, 2024
bf07754
Update drowndown arrow margins
jloh02 Aug 31, 2024
fd117ac
Fix button group design
jloh02 Aug 31, 2024
92514e3
Update header text
jloh02 Aug 31, 2024
afb813d
Update tests and snapshots
jloh02 Aug 31, 2024
e7e9a5f
Fix linter issues
jloh02 Aug 31, 2024
d01d5a2
Fix style linter
jloh02 Aug 31, 2024
9e49f06
Fix tests
jloh02 Aug 31, 2024
5360026
Fix infinite update bug
jloh02 Sep 2, 2024
94827b1
Add validation per field
jloh02 Sep 2, 2024
b81ebf1
Add time validation
jloh02 Sep 2, 2024
dde5f23
Fix linters
jloh02 Sep 2, 2024
e445e45
Make fields optional
jloh02 Sep 4, 2024
5e12042
Merge branch 'master' into feat/custom-modules
ravern Sep 11, 2024
88a86c9
Add handling for semester
jloh02 Sep 11, 2024
ee6e3de
Allow custom modules from 0000 to 2359
jloh02 Sep 11, 2024
d8b4467
Add constraint for timings
jloh02 Sep 11, 2024
990ff03
Enable serialization using sync/sharing link
jloh02 Sep 12, 2024
e132346
Only allow valid dropdown timings
jloh02 Sep 12, 2024
8ed9e2d
Fix unit tests
jloh02 Sep 12, 2024
8e0c874
Fix test and styles
jloh02 Sep 12, 2024
dd410ed
Merge branch 'feat/custom-modules' of https://github.com/seanlowjk/nu…
jloh02 Sep 12, 2024
a334558
Move add custom modules button to timetable actions
jloh02 Sep 18, 2024
2f2dca4
Fix time boundaries
jloh02 Sep 18, 2024
07796ea
Fix tests
jloh02 Sep 18, 2024
b9d1994
Fix css alignment
jloh02 Sep 18, 2024
c585467
Fix compilation issues
jloh02 Sep 18, 2024
d47f0a6
Fix color and custom modules serialization
jloh02 Oct 1, 2024
0c0a6b3
Fix custom modules import bugs
jloh02 Oct 1, 2024
4653b7b
Support date entry for special term
jloh02 Oct 4, 2024
8b1586d
Handle wrong semester used
jloh02 Oct 4, 2024
aba5450
Fix date field component month
jloh02 Oct 4, 2024
cef0c02
Fix tests
jloh02 Oct 4, 2024
f2175e5
Merge branch 'master' of https://github.com/jloh02/nusmods into feat/…
jloh02 Jan 12, 2025
b9f80bf
Fix tests
jloh02 Jan 12, 2025
f440c14
Add feature to allow multiple timeslots per custom course
jloh02 Jan 28, 2025
4d60f9b
Fix custom modules modal state and serialization and deserialization
jloh02 Jan 30, 2025
c8e59b0
Merge branch 'master' of https://github.com/jloh02/nusmods into feat/…
jloh02 Jan 30, 2025
2802cbf
Fix tests
jloh02 Jan 30, 2025
22fb5f8
Merge branch 'master' into feat/custom-modules
jloh02 Feb 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions website/src/actions/__snapshots__/timetables.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,44 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`addCustomModule should add the custom module defined 1`] = `
{
"payload": {
"lessons": [
{
"classNo": "01",
"day": "Monday",
"endTime": "0900",
"isCustom": true,
"lessonType": "Lecture",
"moduleCode": "CS1101S",
"startTime": "0800",
"title": "Programming Methodology",
"venue": "COM1-0330",
"weeks": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
],
},
],
"moduleCode": "CS1101S",
"semester": 2,
"title": "Programming Methodology",
},
"type": "ADD_CUSTOM_MODULE",
}
`;

exports[`cancelModifyLesson should not have payload 1`] = `
{
"payload": null,
Expand All @@ -19,6 +58,16 @@ exports[`changeLesson should return updated information to change lesson 1`] = `
}
`;

exports[`deleteCustomModule should modify the custom module 1`] = `
{
"payload": {
"moduleCode": "CS1101S",
"semester": 2,
},
"type": "DELETE_CUSTOM_MODULE",
}
`;

exports[`fetchTimetableModules should fetch modules 1`] = `
[
[
Expand Down Expand Up @@ -47,6 +96,46 @@ exports[`hide/show timetable modules should dispatch a module code for showing 1
}
`;

exports[`modifyCustomModule should modify the custom module 1`] = `
{
"payload": {
"lessons": [
{
"classNo": "01",
"day": "Monday",
"endTime": "0900",
"isCustom": true,
"lessonType": "Lecture",
"moduleCode": "CS2030",
"startTime": "0800",
"title": "Programming Methodology",
"venue": "COM1-0330",
"weeks": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
],
},
],
"moduleCode": "CS2030",
"oldModuleCode": "CS1101S",
"semester": 2,
"title": "Programming Methodology",
},
"type": "MODIFY_CUSTOM_MODULE",
}
`;

exports[`modifyLesson should return lesson payload 1`] = `
{
"payload": {
Expand Down
56 changes: 55 additions & 1 deletion website/src/actions/timetables.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ModuleCode, Semester } from 'types/modules';
import { SemTimetableConfig, Lesson } from 'types/timetables';
import { SemTimetableConfig, Lesson, CustomModuleLesson } from 'types/timetables';

import lessons from '__mocks__/lessons-array.json';
import { CS1010S, CS3216 } from '__mocks__/modules';
Expand Down Expand Up @@ -129,6 +129,60 @@ describe('hide/show timetable modules', () => {
});
});

describe(actions.addCustomModule, () => {
const semester: Semester = 2;
const moduleCode: ModuleCode = 'CS1101S';
const lesson: CustomModuleLesson = {
classNo: '01',
day: 'Monday',
startTime: '0800',
endTime: '0900',
lessonType: 'Lecture',
venue: 'COM1-0330',
moduleCode,
title: 'Programming Methodology',
isCustom: true,
weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
};

test('should add the custom module defined', () => {
expect(actions.addCustomModule(semester, moduleCode, lesson.title, [lesson])).toMatchSnapshot();
});
});

describe(actions.modifyCustomModule, () => {
const semester: Semester = 2;
const moduleCode: ModuleCode = 'CS1101S';
const newModuleCode: ModuleCode = 'CS2030';
const lesson: CustomModuleLesson = {
classNo: '01',
day: 'Monday',
startTime: '0800',
endTime: '0900',
lessonType: 'Lecture',
venue: 'COM1-0330',
moduleCode: newModuleCode,
title: 'Programming Methodology',
isCustom: true,
weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
};

test('should modify the custom module', () => {
expect(
actions.modifyCustomModule(semester, moduleCode, newModuleCode, lesson.title, [lesson]),
).toMatchSnapshot();
});
});

describe(actions.deleteCustomModule, () => {
const semester: Semester = 2;
const moduleCode: ModuleCode = 'CS1101S';

test('should modify the custom module', () => {
expect(actions.deleteCustomModule(semester, moduleCode)).toMatchSnapshot();
});
});

describe(actions.fetchTimetableModules, () => {
const moduleCodes: any = {
CS1010S: {},
Expand Down
81 changes: 77 additions & 4 deletions website/src/actions/timetables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@
TaModulesConfig,
} from 'types/timetables';
import type { Dispatch, GetState } from 'types/redux';
import type { ColorMapping } from 'types/reducers';
import type { ClassNo, LessonType, Module, ModuleCode, Semester } from 'types/modules';
import type { ColorMapping, CustomModuleLessonData } from 'types/reducers';
import type {
ClassNo,
CustomLesson,
LessonType,
Module,
ModuleCode,
Semester,
} from 'types/modules';

import { fetchModule } from 'actions/moduleBank';
import { openNotification } from 'actions/app';
Expand All @@ -24,19 +31,22 @@
// Actions that should not be used directly outside of thunks
export const SET_TIMETABLE = 'SET_TIMETABLE' as const;
export const ADD_MODULE = 'ADD_MODULE' as const;
export const SET_CUSTOM_IMPORTED = 'SET_CUSTOM_IMPORTED' as const;
export const SET_HIDDEN_IMPORTED = 'SET_HIDDEN_IMPORTED' as const;
export const SET_TA_IMPORTED = 'SET_TA_IMPORTED' as const;
export const TEMP_IMPORTED_SEM = 'TEMP_IMPORTED_SEM' as const;
export const Internal = {
setTimetable(
semester: Semester,
timetable: SemTimetableConfig | undefined,
colors?: ColorMapping,
hiddenModules?: ModuleCode[],
customModules?: CustomModuleLessonData,
taModules?: TaModulesConfig,
) {
return {
type: SET_TIMETABLE,
payload: { semester, timetable, colors, hiddenModules, taModules },
payload: { semester, timetable, colors, hiddenModules, customModules, taModules },
};
},

Expand Down Expand Up @@ -172,7 +182,8 @@
semester,
validatedTimetable,
colors,
getState().timetables.hidden[semester] ?? [],
getState().timetables.hidden[TEMP_IMPORTED_SEM] || [],
getState().timetables.customModules[TEMP_IMPORTED_SEM] || [],
getState().timetables.ta[semester] ?? {},
),
);
Expand Down Expand Up @@ -219,6 +230,20 @@
};
}

export function setCustomModulesFromImport(
semester: Semester,
customModules: CustomModuleLessonData,
) {
return (dispatch: Dispatch) => dispatch(setCustomImported(semester, customModules));

Check warning on line 237 in website/src/actions/timetables.ts

View check run for this annotation

Codecov / codecov/patch

website/src/actions/timetables.ts#L236-L237

Added lines #L236 - L237 were not covered by tests
}

export function setCustomImported(semester: Semester, customModules: CustomModuleLessonData) {
return {

Check warning on line 241 in website/src/actions/timetables.ts

View check run for this annotation

Codecov / codecov/patch

website/src/actions/timetables.ts#L240-L241

Added lines #L240 - L241 were not covered by tests
type: SET_CUSTOM_IMPORTED,
payload: { semester, customModules },
};
}

export function setHiddenModulesFromImport(semester: Semester, hiddenModules: ModuleCode[]) {
return (dispatch: Dispatch) => dispatch(setHiddenImported(semester, hiddenModules));
}
Expand Down Expand Up @@ -273,6 +298,54 @@
};
}

export const ADD_CUSTOM_MODULE = 'ADD_CUSTOM_MODULE' as const;
export function addCustomModule(
semester: Semester,
moduleCode: ModuleCode,
title: string,
lessons: CustomLesson[],
) {
return {
type: ADD_CUSTOM_MODULE,
payload: {
semester,
moduleCode,
title,
lessons,
},
};
}

export const MODIFY_CUSTOM_MODULE = 'MODIFY_CUSTOM_MODULE' as const;
export function modifyCustomModule(
semester: Semester,
oldModuleCode: ModuleCode,
moduleCode: ModuleCode,
title: string,
lessons: CustomLesson[],
) {
return {
type: MODIFY_CUSTOM_MODULE,
payload: {
semester,
oldModuleCode,
moduleCode,
title,
lessons,
},
};
}

export const DELETE_CUSTOM_MODULE = 'DELETE_CUSTOM_MODULE' as const;
export function deleteCustomModule(semester: Semester, moduleCode: ModuleCode) {
return {
type: DELETE_CUSTOM_MODULE,
payload: {
semester,
moduleCode,
},
};
}
export const ADD_TA_LESSON_IN_TIMETABLE = 'ADD_TA_LESSON_IN_TIMETABLE' as const;
export function addTaLessonInTimetable(
semester: Semester,
Expand Down
7 changes: 5 additions & 2 deletions website/src/entry/export/TimetableOnly.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
semester: 1,
timetable: {},
colors: {},
custom: {},
hidden: [],
ta: {},
};
Expand All @@ -27,8 +28,9 @@
const { store } = this.props;
const theme = store.getState().theme.id;

const { semester, timetable, colors, hidden, ta } = this.state;
const filledColors = fillColorMapping(timetable, colors);
// TODO handle exportable custom modules
const { semester, timetable, colors, hidden, ta, custom } = this.state;
const filledColors = fillColorMapping(timetable, colors, []);

Check warning on line 33 in website/src/entry/export/TimetableOnly.tsx

View check run for this annotation

Codecov / codecov/patch

website/src/entry/export/TimetableOnly.tsx#L32-L33

Added lines #L32 - L33 were not covered by tests

return (
<MemoryRouter initialEntries={['https://nusmods.com']}>
Expand All @@ -39,6 +41,7 @@
semester={semester}
timetable={timetable}
colors={filledColors}
customImportedModules={custom}
hiddenImportedModules={hidden}
taImportedModules={ta}
readOnly
Expand Down
1 change: 1 addition & 0 deletions website/src/reducers/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ test('reducers should set export data state', () => {
},
},
},
customModules: {},
colors: {
[1]: {
CS3216: 1,
Expand Down
4 changes: 2 additions & 2 deletions website/src/reducers/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { REMOVE_MODULE, SET_TIMETABLE } from 'actions/timetables';
import { DELETE_CUSTOM_MODULE, REMOVE_MODULE, SET_TIMETABLE } from 'actions/timetables';

import persistReducer from 'storage/persistReducer';
import { State } from 'types/state';
Expand Down Expand Up @@ -29,7 +29,7 @@ const planner = persistReducer('planner', plannerReducer, plannerPersistConfig);
const defaultState = {} as unknown as State;
const undoReducer = createUndoReducer<State>({
limit: 1,
actionsToWatch: [REMOVE_MODULE, SET_TIMETABLE],
actionsToWatch: [REMOVE_MODULE, SET_TIMETABLE, DELETE_CUSTOM_MODULE],
storedKeyPaths: ['timetables', 'theme.colors'],
});

Expand Down
Loading