diff --git a/frontend/src/assets/GraphicalSelectorHelp/step2-dark.jpg b/frontend/src/assets/GraphicalSelectorHelp/step2-dark.jpg index 0c902df89..61682f46f 100644 Binary files a/frontend/src/assets/GraphicalSelectorHelp/step2-dark.jpg and b/frontend/src/assets/GraphicalSelectorHelp/step2-dark.jpg differ diff --git a/frontend/src/assets/GraphicalSelectorHelp/step2-light.jpg b/frontend/src/assets/GraphicalSelectorHelp/step2-light.jpg index 619b9ca06..3bc973f24 100644 Binary files a/frontend/src/assets/GraphicalSelectorHelp/step2-light.jpg and b/frontend/src/assets/GraphicalSelectorHelp/step2-light.jpg differ diff --git a/frontend/src/assets/GraphicalSelectorHelp/step3-dark.jpg b/frontend/src/assets/GraphicalSelectorHelp/step3-dark.jpg index 1bb9c0f73..0c902df89 100644 Binary files a/frontend/src/assets/GraphicalSelectorHelp/step3-dark.jpg and b/frontend/src/assets/GraphicalSelectorHelp/step3-dark.jpg differ diff --git a/frontend/src/assets/GraphicalSelectorHelp/step3-light.jpg b/frontend/src/assets/GraphicalSelectorHelp/step3-light.jpg index bb42e3403..619b9ca06 100644 Binary files a/frontend/src/assets/GraphicalSelectorHelp/step3-light.jpg and b/frontend/src/assets/GraphicalSelectorHelp/step3-light.jpg differ diff --git a/frontend/src/assets/GraphicalSelectorHelp/step4-dark.jpg b/frontend/src/assets/GraphicalSelectorHelp/step4-dark.jpg index fce807045..1f2f71491 100644 Binary files a/frontend/src/assets/GraphicalSelectorHelp/step4-dark.jpg and b/frontend/src/assets/GraphicalSelectorHelp/step4-dark.jpg differ diff --git a/frontend/src/assets/GraphicalSelectorHelp/step4-light.jpg b/frontend/src/assets/GraphicalSelectorHelp/step4-light.jpg index 83181379c..13a079ba0 100644 Binary files a/frontend/src/assets/GraphicalSelectorHelp/step4-light.jpg and b/frontend/src/assets/GraphicalSelectorHelp/step4-light.jpg differ diff --git a/frontend/src/assets/GraphicalSelectorHelp/step5-dark.jpg b/frontend/src/assets/GraphicalSelectorHelp/step5-dark.jpg new file mode 100644 index 000000000..fce807045 Binary files /dev/null and b/frontend/src/assets/GraphicalSelectorHelp/step5-dark.jpg differ diff --git a/frontend/src/assets/GraphicalSelectorHelp/step5-light.jpg b/frontend/src/assets/GraphicalSelectorHelp/step5-light.jpg new file mode 100644 index 000000000..83181379c Binary files /dev/null and b/frontend/src/assets/GraphicalSelectorHelp/step5-light.jpg differ diff --git a/frontend/src/pages/GraphicalSelector/CourseGraph/CourseGraph.tsx b/frontend/src/pages/GraphicalSelector/CourseGraph/CourseGraph.tsx index 7c56eb2d1..e0e9a2d7b 100644 --- a/frontend/src/pages/GraphicalSelector/CourseGraph/CourseGraph.tsx +++ b/frontend/src/pages/GraphicalSelector/CourseGraph/CourseGraph.tsx @@ -49,7 +49,7 @@ const CourseGraph = ({ onNodeClick, handleToggleFullscreen, fullscreen, focused const previousTheme = useRef(theme); const { programCode, specs } = useSelector((state: RootState) => state.degree); const { courses: plannedCourses } = useSelector((state: RootState) => state.planner); - const { degree, planner } = useSelector((state: RootState) => state); + const { degree, planner, courses } = useSelector((state: RootState) => state); const windowSize = useAppWindowSize(); const graphRef = useRef(null); @@ -114,7 +114,10 @@ const CourseGraph = ({ onNodeClick, handleToggleFullscreen, fullscreen, focused }); graphRef.current?.getNodes().forEach((n) => { const courseId = n.getID(); - graphRef.current?.updateItem(n as Item, mapNodeRestore(courseId, plannedCourses, theme)); + graphRef.current?.updateItem( + n as Item, + mapNodeRestore(courseId, plannedCourses, courses.courses, theme) + ); graphRef.current?.updateItem(n as Item, mapNodeOpacity(courseId, 1)); n.toFront(); }); @@ -144,8 +147,7 @@ const CourseGraph = ({ onNodeClick, handleToggleFullscreen, fullscreen, focused graphRef.current?.paint(); }; - // courses is a list of course codes - const initialiseGraph = async (courses: string[], courseEdges: CourseEdge[]) => { + const initialiseGraph = async (courseCodes: string[], courseEdges: CourseEdge[]) => { const container = containerRef.current; if (!container) return; @@ -180,9 +182,8 @@ const CourseGraph = ({ onNodeClick, handleToggleFullscreen, fullscreen, focused }; graphRef.current = new Graph(graphArgs); - const data = { - nodes: courses.map((c) => mapNodeStyle(c, plannedCourses, theme)), + nodes: courseCodes.map((c) => mapNodeStyle(c, plannedCourses, courses.courses, theme)), edges: courseEdges }; @@ -221,7 +222,10 @@ const CourseGraph = ({ onNodeClick, handleToggleFullscreen, fullscreen, focused const repaintCanvas = async () => { const nodes = graphRef.current?.getNodes(); nodes?.map((n) => - graphRef.current?.updateItem(n, mapNodeStyle(n.getID(), plannedCourses, theme)) + graphRef.current?.updateItem( + n, + mapNodeStyle(n.getID(), plannedCourses, courses.courses, theme) + ) ); graphRef.current?.off('node:mouseenter'); @@ -245,9 +249,11 @@ const CourseGraph = ({ onNodeClick, handleToggleFullscreen, fullscreen, focused const res = await axios.get( `/programs/graph/${programCode}/${specs.join('+')}` ); - const { edges, courses } = res.data; + const { edges } = res.data; makePrerequisitesMap(edges); - if (courses.length !== 0 && edges.length !== 0) initialiseGraph(courses, edges); + if (res.data.courses.length !== 0 && edges.length !== 0) { + initialiseGraph(res.data.courses, edges); + } } catch (e) { // eslint-disable-next-line no-console console.error('Error at setupGraph', e); @@ -260,7 +266,7 @@ const CourseGraph = ({ onNodeClick, handleToggleFullscreen, fullscreen, focused previousTheme.current = theme; repaintCanvas(); } - }, [onNodeClick, plannedCourses, programCode, specs, theme, prerequisites]); + }, [onNodeClick, plannedCourses, programCode, specs, theme, prerequisites, courses]); const showAllCourses = () => { if (!graphRef.current) return; diff --git a/frontend/src/pages/GraphicalSelector/CourseGraph/graph.ts b/frontend/src/pages/GraphicalSelector/CourseGraph/graph.ts index db63807ca..43ee79256 100644 --- a/frontend/src/pages/GraphicalSelector/CourseGraph/graph.ts +++ b/frontend/src/pages/GraphicalSelector/CourseGraph/graph.ts @@ -1,4 +1,5 @@ import type { Arrow } from '@antv/g6'; +import { CourseValidation } from 'types/courses'; import { PlannerCourse } from 'types/planner'; const plannedNode = { @@ -31,6 +32,19 @@ const lockedNode = (theme: string) => ({ } }); +const unlockedNode = (theme: string) => ({ + style: { + fill: theme === 'light' ? '#fbefff' : '#381f56', + stroke: theme === 'light' ? '#9254de' : '#d7b7fd', + lineWidth: 1 + }, + labelCfg: { + style: { + fill: theme === 'light' ? '#9254de' : '#d7b7fd' + } + } +}); + const prereqNode = (theme: string) => ({ style: { stroke: theme === 'light' ? '#000' : '#fff', @@ -43,6 +57,22 @@ const sameNode = (courseCode: string) => ({ label: courseCode }); +const plannedLabel = { + labelCfg: { + style: { + fill: '#fff' + } + } +}; + +const lockedLabel = (theme: string) => ({ + labelCfg: { + style: { + fill: theme === 'light' ? '#9254de' : '#d7b7fd' + } + } +}); + const nodeStateStyles = { hover: { fill: '#b37feb', @@ -99,10 +129,14 @@ const edgeUnhoverStyle = (arrow: typeof Arrow, theme: string, id: string) => { const mapNodeStyle = ( courseCode: string, plannedCourses: Record, + courses: Record, theme: string ) => { - // If planned, keep default styling. Otherwise apply lockedNode styling. - if (plannedCourses[courseCode]) return sameNode(courseCode); + const isPlanned = plannedCourses[courseCode]; + const isUnlocked = courses[courseCode]?.unlocked; + + if (isPlanned) return sameNode(courseCode); + if (isUnlocked) return { ...sameNode(courseCode), ...unlockedNode(theme) }; return { ...sameNode(courseCode), ...lockedNode(theme) }; }; @@ -116,9 +150,14 @@ const mapNodePrereq = (courseCode: string, theme: string) => { const mapNodeRestore = ( courseCode: string, plannedCourses: Record, + courses: Record, theme: string ) => { - if (plannedCourses[courseCode]) return { ...sameNode(courseCode), ...plannedNode }; + const isPlanned = plannedCourses[courseCode]; + const isUnlocked = courses[courseCode]?.unlocked; + + if (isPlanned) return { ...sameNode(courseCode), ...plannedNode }; + if (isUnlocked) return { ...sameNode(courseCode), ...unlockedNode(theme) }; return { ...sameNode(courseCode), ...lockedNode(theme) }; }; @@ -145,11 +184,7 @@ const edgeOpacity = (id: string, opacity: number) => ({ const nodeLabelHoverStyle = (courseCode: string) => ({ ...sameNode(courseCode), - labelCfg: { - style: { - fill: '#fff' - } - } + ...plannedLabel }); const nodeLabelUnhoverStyle = ( @@ -161,20 +196,12 @@ const nodeLabelUnhoverStyle = ( // uses default node style with label color changed return { ...sameNode(courseCode), - labelCfg: { - style: { - fill: '#fff' - } - } + ...plannedLabel }; } return { ...sameNode(courseCode), - labelCfg: { - style: { - fill: theme === 'light' ? '#9254de' : '#d7b7fd' - } - } + ...lockedLabel(theme) }; }; diff --git a/frontend/src/pages/GraphicalSelector/HowToUse/HowToUse.tsx b/frontend/src/pages/GraphicalSelector/HowToUse/HowToUse.tsx index 9459f7420..9bd107af9 100644 --- a/frontend/src/pages/GraphicalSelector/HowToUse/HowToUse.tsx +++ b/frontend/src/pages/GraphicalSelector/HowToUse/HowToUse.tsx @@ -9,6 +9,8 @@ import step3Dark from 'assets/GraphicalSelectorHelp/step3-dark.jpg'; import step3Light from 'assets/GraphicalSelectorHelp/step3-light.jpg'; import step4Dark from 'assets/GraphicalSelectorHelp/step4-dark.jpg'; import step4Light from 'assets/GraphicalSelectorHelp/step4-light.jpg'; +import step5Dark from 'assets/GraphicalSelectorHelp/step5-dark.jpg'; +import step5Light from 'assets/GraphicalSelectorHelp/step5-light.jpg'; import type { RootState } from 'config/store'; import CS from '../common/styles'; import S from './styles'; @@ -19,12 +21,11 @@ const HowToUse = () => { const { theme } = useSelector((state: RootState) => state.settings); const step = (num: number) => { - let url = ''; - if (num === 1) url = theme === 'light' ? step1Light : step1Dark; - if (num === 2) url = theme === 'light' ? step2Light : step2Dark; - if (num === 3) url = theme === 'light' ? step3Light : step3Dark; - if (num === 4) url = theme === 'light' ? step4Light : step4Dark; - return url; + const pics = { + light: [step1Light, step2Light, step3Light, step4Light, step5Light], + dark: [step1Dark, step2Dark, step3Dark, step4Dark, step5Dark] + }; + return theme === 'light' ? pics.light[num - 1] : pics.dark[num - 1]; }; return ( @@ -41,17 +42,21 @@ const HowToUse = () => {
- 2. Use the search bar to bring up the courses. + 2. Courses can be planned, unlocked or locked.
- +
- 3. Hover over a course to see related courses. + 3. Use the search bar to bring up the courses.
- +
- 4. Click the course to view the course information! + 4. Hover over a course to see related courses.
- + +
+ 5. Click the course to view the course information! +
+ );