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

feat(website): Add TA mode #3875

Merged
merged 30 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
21c9a05
Add TA mode to hide lectures
leslieyip02 Nov 28, 2024
b372c98
Fix false exam clash on hidden and TA modules
leslieyip02 Nov 28, 2024
d829507
Add visual indicator for TA modules
leslieyip02 Nov 29, 2024
69a12f6
Update export to support TA modules
leslieyip02 Nov 29, 2024
b8ff368
Add choice of lesson types to TA mode
leslieyip02 Dec 1, 2024
e2df4f6
Fix missing property
leslieyip02 Dec 1, 2024
637f96d
Fix type mismatch
leslieyip02 Dec 1, 2024
f06d98e
Merge branch 'master' into add-ta-mode
zwliew Dec 16, 2024
3eae3a6
Merge branch 'master' into add-ta-mode
zwliew Dec 17, 2024
012d9df
Merge branch 'master' of https://github.com/nusmodifications/nusmods …
leslieyip02 Dec 22, 2024
0f7c4e0
Merge branch 'add-ta-mode' of https://github.com/leslieyip02/nusmods …
leslieyip02 Dec 22, 2024
b311cb1
Allow multiple lesson slots of the same type
leslieyip02 Dec 22, 2024
8e54fed
Improve toggle
leslieyip02 Dec 23, 2024
50d72ac
Merge branch 'master' into add-ta-mode
zwliew Dec 24, 2024
2f36ae6
Replace toggle by lesson type to toggle by module
leslieyip02 Dec 27, 2024
71e4355
Merge branch 'master' into add-ta-mode
leslieyip02 Dec 27, 2024
571e8d5
Merge branch 'master' into add-ta-mode
leslieyip02 Dec 29, 2024
eadbd5b
Update export
leslieyip02 Dec 29, 2024
4995b68
Merge branch 'master' into add-ta-mode
leslieyip02 Jan 2, 2025
d323978
Update lesson hydrate functions
leslieyip02 Jan 2, 2025
f7c4f80
Merge branch 'master' into add-ta-mode
zwliew Jan 3, 2025
826a51b
Fix test failures
zwliew Jan 3, 2025
28c0506
Use proper types
zwliew Jan 3, 2025
c3fcb79
Clean up some code
zwliew Jan 3, 2025
77b72b8
Clean up more code
zwliew Jan 3, 2025
48f5a98
Rename set/unset -> enable/disable
zwliew Jan 3, 2025
fc91c31
More clean ups
zwliew Jan 3, 2025
29a6850
More clean ups
zwliew Jan 3, 2025
b769ddc
Final clean ups
zwliew Jan 3, 2025
6c07267
Remove unnecessary code
zwliew Jan 3, 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
10 changes: 9 additions & 1 deletion website/src/actions/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { captureException } from 'utils/error';
import retryImport from 'utils/retryImport';
import { getSemesterTimetableLessons } from 'selectors/timetables';
import { TaModuleConfig } from 'types/timetables';
import { SET_EXPORTED_DATA } from './constants';

function downloadUrl(blob: Blob, filename: string) {
Expand Down Expand Up @@ -32,11 +33,18 @@
const state = getState();
const { modules } = state.moduleBank;
const hiddenModules: ModuleCode[] = state.timetables.hidden[semester] || [];
const taModules: TaModuleConfig = state.timetables.ta[semester] || {};

Check warning on line 36 in website/src/actions/export.ts

View check run for this annotation

Codecov / codecov/patch

website/src/actions/export.ts#L36

Added line #L36 was not covered by tests

const timetable = getSemesterTimetableLessons(state)(semester);
const timetableWithLessons = hydrateSemTimetableWithLessons(timetable, modules, semester);

const events = icalUtils.default(semester, timetableWithLessons, modules, hiddenModules);
const events = icalUtils.default(

Check warning on line 41 in website/src/actions/export.ts

View check run for this annotation

Codecov / codecov/patch

website/src/actions/export.ts#L41

Added line #L41 was not covered by tests
semester,
timetableWithLessons,
modules,
hiddenModules,
taModules,
);
const cal = ical.default({
domain: 'nusmods.com',
prodId: '//NUSMods//NUSMods//EN',
Expand Down
59 changes: 51 additions & 8 deletions website/src/actions/timetables.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { each, flatMap } from 'lodash';

import type { ColorIndex, Lesson, ModuleLessonConfig, SemTimetableConfig } from 'types/timetables';
import type {
ColorIndex,
Lesson,
ModuleLessonConfig,
SemTimetableConfig,
TaModuleConfig,
} 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';
Expand All @@ -19,17 +25,18 @@
export const SET_TIMETABLE = 'SET_TIMETABLE' as const;
export const ADD_MODULE = 'ADD_MODULE' as const;
export const SET_HIDDEN_IMPORTED = 'SET_HIDDEN_IMPORTED' as const;
export const HIDDEN_IMPORTED_SEM = 'HIDDEN_IMPORTED_SEM' as const;
export const SET_TA_IMPORTED = 'SET_TA_IMPORTED' as const;
export const Internal = {
setTimetable(
semester: Semester,
timetable: SemTimetableConfig | undefined,
colors?: ColorMapping,
hiddenModules?: ModuleCode[],
taModules?: TaModuleConfig,
) {
return {
type: SET_TIMETABLE,
payload: { semester, timetable, colors, hiddenModules },
payload: { semester, timetable, colors, hiddenModules, taModules },
};
},

Expand Down Expand Up @@ -165,7 +172,8 @@
semester,
validatedTimetable,
colors,
getState().timetables.hidden[HIDDEN_IMPORTED_SEM] || [],
getState().timetables.hidden[semester] || [],
getState().timetables.ta[semester] || {},
),
);
};
Expand Down Expand Up @@ -211,14 +219,25 @@
};
}

export function setHiddenModulesFromImport(hiddenModules: ModuleCode[]) {
return (dispatch: Dispatch) => dispatch(setHiddenImported(hiddenModules));
export function setHiddenModulesFromImport(semester: Semester, hiddenModules: ModuleCode[]) {
return (dispatch: Dispatch) => dispatch(setHiddenImported(semester, hiddenModules));

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

View check run for this annotation

Codecov / codecov/patch

website/src/actions/timetables.ts#L222-L223

Added lines #L222 - L223 were not covered by tests
}

export function setHiddenImported(hiddenModules: ModuleCode[]) {
export function setHiddenImported(semester: Semester, hiddenModules: ModuleCode[]) {
return {
type: SET_HIDDEN_IMPORTED,
payload: { semester: HIDDEN_IMPORTED_SEM, hiddenModules },
payload: { semester, hiddenModules },
};
}

export function setTaModulesFromImport(semester: Semester, taModules: TaModuleConfig) {
return (dispatch: Dispatch) => dispatch(setTaImported(semester, taModules));

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

View check run for this annotation

Codecov / codecov/patch

website/src/actions/timetables.ts#L233-L234

Added lines #L233 - L234 were not covered by tests
}

export function setTaImported(semester: Semester, taModules: TaModuleConfig) {
return {

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

View check run for this annotation

Codecov / codecov/patch

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

Added lines #L237 - L238 were not covered by tests
type: SET_TA_IMPORTED,
payload: { semester, taModules },
};
}

Expand Down Expand Up @@ -253,3 +272,27 @@
payload: { moduleCode, semester },
};
}

export const ADD_TA_LESSON_IN_TIMETABLE = 'ADD_TA_LESSON_IN_TIMETABLE' as const;
export function addTaLessonInTimetable(
semester: Semester,
moduleCode: ModuleCode,
lessonType: LessonType,
) {
return {
type: ADD_TA_LESSON_IN_TIMETABLE,
payload: { moduleCode, lessonType, semester },
};
}

export const REMOVE_TA_LESSON_IN_TIMETABLE = 'REMOVE_TA_LESSON_IN_TIMETABLE' as const;
export function removeTaLessonInTimetable(
semester: Semester,
moduleCode: ModuleCode,
lessonType: LessonType,
) {
return {
type: REMOVE_TA_LESSON_IN_TIMETABLE,
payload: { moduleCode, lessonType, semester },
};
}
1 change: 1 addition & 0 deletions website/src/entry/export/TimetableOnly.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default class TimetableOnly extends Component<Props, State> {
timetable={timetable}
colors={timetableColors}
hiddenImportedModules={null}
taImportedModules={null}
readOnly
/>
</div>
Expand Down
8 changes: 8 additions & 0 deletions website/src/reducers/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ const exportData: ExportData = {
PC1222: 2,
},
hidden: ['PC1222'],
ta: {
CS1010S: ['Tutorial'],
},
theme: {
id: 'google',
timetableOrientation: VERTICAL,
Expand Down Expand Up @@ -74,6 +77,11 @@ test('reducers should set export data state', () => {
},
},
hidden: { [1]: ['PC1222'] },
ta: {
[1]: {
CS1010S: ['Tutorial'],
},
},
academicYear: expect.any(String),
archive: {},
});
Expand Down
102 changes: 68 additions & 34 deletions website/src/reducers/timetables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import {
setLessonConfig,
showLessonInTimetable,
setHiddenImported,
HIDDEN_IMPORTED_SEM,
Internal,
addTaLessonInTimetable,
removeTaLessonInTimetable,
} from 'actions/timetables';
import { TimetablesState } from 'types/reducers';
import config from 'config';
Expand Down Expand Up @@ -73,6 +74,7 @@ describe('color reducers', () => {
timetable: { CS1010S: {} },
colors: { CS1010S: 0 },
hiddenModules: [],
taModules: {},
},
}).colors[1],
).toEqual({
Expand Down Expand Up @@ -124,6 +126,54 @@ describe('hidden module reducer', () => {
});
});

describe('TA module reducer', () => {
const withTaModules: TimetablesState = {
...initialState,
ta: { [1]: { CS1010S: ['Tutorial'] }, [2]: { CS1010S: ['Tutorial'] } },
};

test('should update TA modules', () => {
expect(reducer(initialState, addTaLessonInTimetable(1, 'CS3216', 'Tutorial'))).toHaveProperty(
'ta.1',
{ CS3216: ['Tutorial'] },
);

expect(
reducer(initialState, removeTaLessonInTimetable(1, 'CS1010S', 'Tutorial')),
).toMatchObject({
ta: {
[1]: {},
},
});

expect(
reducer(withTaModules, removeTaLessonInTimetable(1, 'CS1010S', 'Tutorial')),
).toMatchObject({
ta: {
[1]: {},
[2]: { CS1010S: ['Tutorial'] },
},
});
});

test('should remove modules from list when modules are removed', () => {
expect(
reducer(
{
...initialState,
ta: { [1]: { CS1010S: ['Tutorial'] }, [2]: { CS1010S: ['Tutorial'] } },
},
removeModule(1, 'CS1010S'),
),
).toMatchObject({
ta: {
[1]: {},
[2]: { CS1010S: ['Tutorial'] },
},
});
});
});

describe('lesson reducer', () => {
test('should allow lesson config to be set', () => {
expect(
Expand Down Expand Up @@ -213,6 +263,7 @@ describe('stateReconciler', () => {
hidden: {
[1]: ['CS1010S'],
},
ta: {},
academicYear: config.academicYear,
archive: oldArchive,
};
Expand Down Expand Up @@ -249,53 +300,36 @@ describe('stateReconciler', () => {
});

describe('import timetable', () => {
const stateWithHidden = {
...initialState,
hidden: {
[1]: ['CS1101S', 'CS1231S'],
},
};

test('should have hidden modules set when importing hidden', () => {
expect(reducer(initialState, setHiddenImported(['CS1101S', 'CS1231S'])).hidden).toMatchObject({
[HIDDEN_IMPORTED_SEM]: ['CS1101S', 'CS1231S'],
expect(
reducer(initialState, setHiddenImported(1, ['CS1101S', 'CS1231S'])).hidden,
).toMatchObject({
[1]: ['CS1101S', 'CS1231S'],
});

// Should change hidden modules when a new set of modules is imported
expect(
reducer(
{
...initialState,
hidden: {
[HIDDEN_IMPORTED_SEM]: ['CS1101S', 'CS1231S'],
},
},
setHiddenImported(['CS2100', 'CS2103T']),
).hidden,
reducer(stateWithHidden, setHiddenImported(1, ['CS2100', 'CS2103T'])).hidden,
).toMatchObject({
[HIDDEN_IMPORTED_SEM]: ['CS2100', 'CS2103T'],
[1]: ['CS2100', 'CS2103T'],
});

// should delete hidden modules when there are none
expect(
reducer(
{
...initialState,
hidden: {
[HIDDEN_IMPORTED_SEM]: ['CS1101S', 'CS1231S'],
},
},
setHiddenImported([]),
).hidden,
).toMatchObject({
[HIDDEN_IMPORTED_SEM]: [],
expect(reducer(stateWithHidden, setHiddenImported(1, [])).hidden).toMatchObject({
[1]: [],
});
});

test('should copy over hidden modules when deciding to replace saved timetable', () => {
expect(
reducer(
{
...initialState,
hidden: {
[HIDDEN_IMPORTED_SEM]: ['CS1101S', 'CS1231S'],
},
},
Internal.setTimetable(1, {}, {}, ['CS1101S', 'CS1231S']),
).hidden,
reducer(stateWithHidden, Internal.setTimetable(1, {}, {}, stateWithHidden.hidden[1])).hidden,
).toMatchObject({
'1': ['CS1101S', 'CS1231S'],
});
Expand Down
Loading