Skip to content

Commit 454c882

Browse files
leslieyip02zwliew
andauthored
fix(website/ta): Hide TA button for non TA-able mods (#3932)
Co-authored-by: Zhao Wei Liew <[email protected]>
1 parent 801e707 commit 454c882

File tree

9 files changed

+151
-24
lines changed

9 files changed

+151
-24
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"acadYear": "2024/2025",
3+
"preclusion": "If undertaking an Undergraduate Degree THEN ( must not have completed EG3601 at a grade of at least D)",
4+
"preclusionRule": "PROGRAM_TYPES IF_IN Undergraduate Degree THEN (COURSES (1) EG3601:D)",
5+
"description": "This course enables students to apply the computing knowledge they have assimilated in class to industrial projects through six-month attachment to companies/organizations. Students under attachment will be jointly guided by supervisors from both the companies/organizations and the school. Their progress on projects will be monitored during attachment, and their performance will be assessed (on Satisfactory/Unsatisfactory (S/U) basis) at the end of the attachment, based on the interim and final project reports. During the attachment, students are not expected to take other courses offered by the university.",
6+
"title": "Advanced Technology Attachment Programme",
7+
"additionalInformation": "",
8+
"department": "SoC Dean's Office",
9+
"faculty": "Computing",
10+
"workload": [0, 0, 0, 30, 0],
11+
"gradingBasisDescription": "Completed Satisfactory/Unsatisfactory",
12+
"prerequisite": "If undertaking an Undergraduate Degree THEN ( must have completed 1 of any Courses (Modules) beginning with UTW1001 at a grade of at least D, CS2101/ES1601/ES2002/ES2007D/ES2531/ES2631/IS2101/NTW2017/NTW2028/NTW2029/NTW2030/NTW2031/NTW2032/NTW2033/NTW2034/UWC2101% at a grade of at least D AND must have completed 1 of BT3103/CS2103/CS2103T/CS2113/CS2113T/IS3106 at a grade of at least D)",
13+
"prerequisiteRule": "PROGRAM_TYPES IF_IN Undergraduate Degree THEN (COURSES (1) IS2101:D, CS2101:D, ES1601:D, ES2002:D, ES2007D:D, ES2531:D, ES2631:D, UTW1001%:D, UWC2101%:D, NTW2017:D, NTW2028:D, NTW2029:D, NTW2030:D, NTW2031:D, NTW2032:D, NTW2033:D, NTW2034:D AND COURSES (1) CS2103:D, CS2103T:D, CS2113:D, CS2113T:D, IS3106:D, BT3103:D)",
14+
"moduleCredit": "12",
15+
"moduleCode": "CP3880",
16+
"semesterData": [
17+
{
18+
"semester": 1,
19+
"timetable": [],
20+
"covidZones": []
21+
},
22+
{
23+
"semester": 2,
24+
"timetable": [],
25+
"covidZones": []
26+
},
27+
{
28+
"semester": 3,
29+
"timetable": [],
30+
"covidZones": []
31+
},
32+
{
33+
"semester": 4,
34+
"timetable": [],
35+
"covidZones": []
36+
}
37+
],
38+
"prereqTree": {
39+
"and": [
40+
{
41+
"or": [
42+
"IS2101:D",
43+
"CS2101:D",
44+
"ES1601:D",
45+
"ES2002:D",
46+
"ES2007D:D",
47+
"ES2531:D",
48+
"ES2631:D",
49+
"UTW1001%:D",
50+
"UWC2101%:D",
51+
"NTW2017:D",
52+
"NTW2028:D",
53+
"NTW2029:D",
54+
"NTW2030:D",
55+
"NTW2031:D",
56+
"NTW2032:D",
57+
"NTW2033:D",
58+
"NTW2034:D"
59+
]
60+
},
61+
{
62+
"or": ["CS2103:D", "CS2103T:D", "CS2113:D", "CS2113T:D", "IS3106:D", "BT3103:D"]
63+
}
64+
]
65+
},
66+
"fulfillRequirements": ["IFS4201"]
67+
}

website/src/__mocks__/modules/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Module } from 'types/modules';
22

33
import ACC2002_JSON from './ACC2002.json';
44
import BFS1001_JSON from './BFS1001.json';
5+
import CP3880_JSON from './CP3880.json';
56
import CS1010A_JSON from './CS1010A.json';
67
import CS1010S_JSON from './CS1010S.json';
78
import CS3216_JSON from './CS3216.json';
@@ -12,14 +13,15 @@ import PC1222_JSON from './PC1222.json';
1213
// Have to cast these as Module explicitly, otherwise TS will try to
1314
// incorrectly infer the shape from the JSON - specifically Weeks will
1415
// not be cast correctly
15-
export const CS1010S: Module = { ...CS1010S_JSON, timestamp: 1572843950000 };
1616
export const ACC2002: Module = { ...ACC2002_JSON, timestamp: 1572843950000 };
1717
export const BFS1001: Module = { ...BFS1001_JSON, timestamp: 1572843950000 };
18+
export const CP3880: Module = { ...CP3880_JSON, timestamp: 1572843950000 };
19+
export const CS1010A: Module = { ...CS1010A_JSON, timestamp: 1572843950000 };
20+
export const CS1010S: Module = { ...CS1010S_JSON, timestamp: 1572843950000 };
1821
export const CS3216: Module = { ...CS3216_JSON, timestamp: 1572843950000 };
22+
export const CS4243: Module = { ...CS4243_JSON, timestamp: 1572843950000 };
1923
export const GES1021: Module = { ...GES1021_JSON, timestamp: 1572843950000 };
2024
export const PC1222: Module = { ...PC1222_JSON, timestamp: 1572843950000 };
21-
export const CS4243: Module = { ...CS4243_JSON, timestamp: 1572843950000 };
22-
export const CS1010A: Module = { ...CS1010A_JSON, timestamp: 1572843950000 };
2325

2426
const modules: Module[] = [ACC2002, BFS1001, CS1010S, CS3216, GES1021, PC1222, CS1010A];
2527
export default modules;

website/src/test-utils/theme.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ export function addColors(
1616
modules: Module[],
1717
isHiddenInTimetable = false,
1818
isTaInTimetable = false,
19+
canTa = false,
1920
): ModuleWithColor[] {
2021
return modules.map((module, index) => ({
2122
...module,
2223
colorIndex: index,
2324
isHiddenInTimetable,
2425
isTaInTimetable,
26+
canTa,
2527
}));
2628
}

website/src/types/views.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export type ModuleWithColor = Module & {
5555
colorIndex: ColorIndex;
5656
isHiddenInTimetable: boolean;
5757
isTaInTimetable: boolean;
58+
canTa: boolean;
5859
};
5960

6061
export type TombstoneModule = ModuleWithColor & {

website/src/utils/modules.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ import {
1616
isGraduateModule,
1717
renderExamDuration,
1818
getExamDuration,
19+
canTa,
1920
} from 'utils/modules';
2021
import { noBreak } from 'utils/react';
2122

2223
import { EVERY_WEEK } from 'test-utils/timetable';
23-
import { CS1010S, CS3216 } from '__mocks__/modules';
24+
import { CP3880, CS1010S, CS3216 } from '__mocks__/modules';
2425
import { Lesson } from 'types/timetables';
2526

2627
const mockLesson = _.cloneDeep(CS1010S.semesterData[0].timetable[0]) as Lesson;
@@ -244,3 +245,23 @@ describe(isGraduateModule, () => {
244245
expect(isGraduateModule({ moduleCode: 'ACC4999X' })).toEqual(false);
245246
});
246247
});
248+
249+
describe(canTa, () => {
250+
const modules = { CP3880, CS1010S, CS3216 };
251+
252+
it('should return true for modules with non-lecture lessons', () => {
253+
expect(canTa(modules, 'CS1010S', 1)).toEqual(true);
254+
});
255+
256+
it('should return false for modules with only lecture lessons', () => {
257+
expect(canTa(modules, 'CS3216', 1)).toEqual(false);
258+
});
259+
260+
it('should return false for modules without lessons', () => {
261+
expect(canTa(modules, 'CP3880', 1)).toEqual(false);
262+
});
263+
264+
it('should return false for unknown modules', () => {
265+
expect(canTa(modules, 'ZZ9999', 1)).toEqual(false);
266+
});
267+
});

website/src/utils/modules.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
import config from 'config';
1313
import { NBSP, noBreak } from 'utils/react';
1414
import { Lesson } from 'types/timetables';
15+
import { ModulesMap } from 'types/reducers';
1516
import { toSingaporeTime } from './timify';
1617

1718
// Look for strings that look like module codes - eg.
@@ -145,3 +146,11 @@ export function getYearsBetween(minYear: string, maxYear: string): string[] {
145146
export function isGraduateModule(module: { moduleCode: ModuleCode }): boolean {
146147
return Boolean(/[A-Z]+(5|6)\d{3}/i.test(module.moduleCode));
147148
}
149+
150+
// A module is TA-able if it has at least 1 non-lecture lesson
151+
export function canTa(modules: ModulesMap, moduleCode: ModuleCode, semester: Semester): boolean {
152+
const module = modules[moduleCode];
153+
if (!module) return false;
154+
const moduleTimetable = getModuleTimetable(module, semester);
155+
return moduleTimetable.some((lesson) => lesson.lessonType !== 'Lecture');
156+
}

website/src/views/timetable/TimetableContent.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
} from 'actions/timetables';
2828
import {
2929
areLessonsSameClass,
30+
canTa,
3031
formatExamDate,
3132
getExamDate,
3233
getModuleTimetable,
@@ -179,6 +180,11 @@ class TimetableContent extends React.Component<Props, State> {
179180

180181
isTaInTimetable = (moduleCode: ModuleCode) => this.props.taInTimetable[moduleCode]?.length > 0;
181182

183+
canTa = (moduleCode: ModuleCode) => {
184+
const { semester, modules } = this.props;
185+
return canTa(modules, moduleCode, semester);
186+
};
187+
182188
// Adds current non lecture lessons as TA lessons
183189
setTaLessonInTimetable = (semester: Semester, moduleCode: ModuleCode) => {
184190
timetableLessonsArray(this.props.timetableWithLessons)
@@ -263,6 +269,7 @@ class TimetableContent extends React.Component<Props, State> {
263269
colorIndex: this.props.colors[module.moduleCode],
264270
isHiddenInTimetable: this.isHiddenInTimetable(module.moduleCode),
265271
isTaInTimetable: this.isTaInTimetable(module.moduleCode),
272+
canTa: this.canTa(module.moduleCode),
266273
});
267274

268275
renderModuleTable = (
@@ -456,6 +463,7 @@ class TimetableContent extends React.Component<Props, State> {
456463
colorIndex: this.props.colors[module.moduleCode],
457464
isHiddenInTimetable: this.isHiddenInTimetable(module.moduleCode),
458465
isTaInTimetable: this.isTaInTimetable(module.moduleCode),
466+
canTa: false,
459467
}))}
460468
/>
461469
) : (

website/src/views/timetable/TimetableModulesTable.test.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ function getModules(wrapper: ShallowWrapper) {
4848
return wrapper.find(`.${styles.modulesTableRow}`).map((ele) => ele.key());
4949
}
5050

51+
function getButtons(wrapper: ShallowWrapper) {
52+
return wrapper.find(`.${styles.moduleActionButtons} > .btn-group`);
53+
}
54+
5155
describe(TimetableModulesTableComponent, () => {
5256
const modules = addColors([CS1010S, CS3216]);
5357

@@ -77,6 +81,7 @@ describe(TimetableModulesTableComponent, () => {
7781
colorIndex: 2,
7882
isHiddenInTimetable: false,
7983
isTaInTimetable: false,
84+
canTa: false,
8085
};
8186

8287
const moduleCodes = getModules(
@@ -85,4 +90,14 @@ describe(TimetableModulesTableComponent, () => {
8590

8691
expect(moduleCodes).toEqual(originalOrder);
8792
});
93+
94+
it('should display buttons correctly', () => {
95+
// TA button is the 3rd button
96+
const withoutTaButton = getButtons(make({ modules: addColors([CS1010S]) }).wrapper);
97+
expect(withoutTaButton.at(0).children()).toHaveLength(2);
98+
99+
const modulesWithTaAbleModule = addColors([CS1010S], false, false, true);
100+
const withTaButton = getButtons(make({ modules: modulesWithTaAbleModule }).wrapper);
101+
expect(withTaButton.at(0).children()).toHaveLength(3);
102+
});
88103
});

website/src/views/timetable/TimetableModulesTable.tsx

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -97,26 +97,28 @@ export const TimetableModulesTableComponent: React.FC<Props> = (props) => {
9797
)}
9898
</button>
9999
</Tooltip>
100-
<Tooltip content={taBtnLabel} touch={['hold', 50]}>
101-
<button
102-
type="button"
103-
className={classnames('btn btn-outline-secondary btn-svg', styles.moduleAction)}
104-
aria-label={taBtnLabel}
105-
onClick={() => {
106-
if (module.isTaInTimetable) {
107-
props.disableTaModeInTimetable(semester, module.moduleCode);
108-
} else {
109-
props.enableTaModeInTimetable(semester, module.moduleCode);
110-
}
111-
}}
112-
>
113-
{module.isTaInTimetable ? (
114-
<BookOpen className={styles.actionIcon} />
115-
) : (
116-
<Book className={styles.actionIcon} />
117-
)}
118-
</button>
119-
</Tooltip>
100+
{module.canTa && (
101+
<Tooltip content={taBtnLabel} touch={['hold', 50]}>
102+
<button
103+
type="button"
104+
className={classnames('btn btn-outline-secondary btn-svg', styles.moduleAction)}
105+
aria-label={taBtnLabel}
106+
onClick={() => {
107+
if (module.isTaInTimetable) {
108+
props.disableTaModeInTimetable(semester, module.moduleCode);
109+
} else {
110+
props.enableTaModeInTimetable(semester, module.moduleCode);
111+
}
112+
}}
113+
>
114+
{module.isTaInTimetable ? (
115+
<BookOpen className={styles.actionIcon} />
116+
) : (
117+
<Book className={styles.actionIcon} />
118+
)}
119+
</button>
120+
</Tooltip>
121+
)}
120122
</div>
121123
</div>
122124
);

0 commit comments

Comments
 (0)