From fc3d5f2938cd6c649aba1882da2a50a3a0743a24 Mon Sep 17 00:00:00 2001 From: "sharonsohxuanhui@hotmail.com" Date: Fri, 26 Jun 2026 21:13:20 +0900 Subject: [PATCH 1/4] feat: add dropped team filter for automated evaluation relation creation --- helpers/relations.test.ts | 58 +++++++++++++++++++++++++++++++++++++++ helpers/relations.ts | 7 +++-- 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 helpers/relations.test.ts diff --git a/helpers/relations.test.ts b/helpers/relations.test.ts new file mode 100644 index 00000000..b919ef33 --- /dev/null +++ b/helpers/relations.test.ts @@ -0,0 +1,58 @@ +/* eslint-disable no-undef */ +import { describe, expect } from "@jest/globals"; +import { generateRoundRobinRelations } from "./relations"; +import { LEVELS_OF_ACHIEVEMENT, Project } from "@/types/projects"; + +const createProject = (id: number, hasDropped = false): Project => ({ + id, + name: `Project ${id}`, + teamName: `Team ${id}`, + proposalPdf: "", + videoUrl: "", + posterUrl: "", + hasDropped, + achievement: LEVELS_OF_ACHIEVEMENT.ARTEMIS, + cohortYear: 2026, + students: [], +}); + +describe("#generateRoundRobinRelations", () => { + it("can generate round robin relations for active teams", () => { + const relations = generateRoundRobinRelations([ + createProject(1), + createProject(2), + createProject(3), + ]); + + expect( + relations.map(({ fromProjectId, toProjectId }) => ({ + fromProjectId, + toProjectId, + })) + ).toEqual([ + { fromProjectId: 1, toProjectId: 2 }, + { fromProjectId: 1, toProjectId: 3 }, + { fromProjectId: 2, toProjectId: 3 }, + { fromProjectId: 2, toProjectId: 1 }, + { fromProjectId: 3, toProjectId: 1 }, + { fromProjectId: 3, toProjectId: 2 }, + ]); + }); + + it("does not create relations for dropped teams", () => { + const relations = generateRoundRobinRelations([ + createProject(1), + createProject(2, true), + createProject(3), + createProject(4), + ]); + + expect(relations).toHaveLength(6); + expect( + relations.some( + ({ fromProjectId, toProjectId }) => + fromProjectId === 2 || toProjectId === 2 + ) + ).toBe(false); + }); +}); diff --git a/helpers/relations.ts b/helpers/relations.ts index a80409a6..6dd4c40e 100644 --- a/helpers/relations.ts +++ b/helpers/relations.ts @@ -21,7 +21,8 @@ export const generateRoundRobinRelations = ( projects: Project[] ): Partial[] => { const relations: Partial[] = []; - const numberOfProjects = projects.length; + const activeProjects = projects.filter((project) => !project.hasDropped); + const numberOfProjects = activeProjects.length; const numberOfEvaluations = Math.min( numberOfProjects - 1, @@ -30,8 +31,8 @@ export const generateRoundRobinRelations = ( for (let i = 0; i < numberOfProjects; i++) { for (let j = 1; j <= numberOfEvaluations; j++) { - const fromProject = projects[i]; - const toProject = projects[(i + j) % numberOfProjects]; + const fromProject = activeProjects[i]; + const toProject = activeProjects[(i + j) % numberOfProjects]; relations.push({ fromProject, toProject, From 25ffc1864cdb6722db16dc8a3975a4f9f84ac978 Mon Sep 17 00:00:00 2001 From: "sharonsohxuanhui@hotmail.com" Date: Fri, 26 Jun 2026 22:00:01 +0900 Subject: [PATCH 2/4] feat: warn when existing relations include dropped teams --- .../AdviserManageRelationsContent.tsx | 9 +++++++ pages/dashboard/administrator.tsx | 25 ++++++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/components/relations/AdviserManageRelationsContent/AdviserManageRelationsContent.tsx b/components/relations/AdviserManageRelationsContent/AdviserManageRelationsContent.tsx index df0013f1..b9d03fc6 100644 --- a/components/relations/AdviserManageRelationsContent/AdviserManageRelationsContent.tsx +++ b/components/relations/AdviserManageRelationsContent/AdviserManageRelationsContent.tsx @@ -21,6 +21,7 @@ import { FC, useState } from "react"; import SidebarActions from "./SidebarActions/SidebarActions"; import { NUMBER_OF_EVALUATIONS_PER_TEAM, + getRelationsWithDroppedTeams, groupRelationsByTeam, } from "@/helpers/relations"; @@ -80,6 +81,7 @@ const AdviserManageRelationsContent: FC = ({ const { teamsThatDoNotSatisfy, satisfies } = checkIfAllTeamsSatisfyRequirements(); + const droppedTeamRelations = getRelationsWithDroppedTeams(relations); return ( <> @@ -88,6 +90,13 @@ const AdviserManageRelationsContent: FC = ({ Summary {`You are currently assigned to ${projects.length} teams`} + {droppedTeamRelations.length > 0 && ( + {`${ + droppedTeamRelations.length + } evaluation relation${ + droppedTeamRelations.length > 1 ? "s" : "" + } include removed teams. Delete or update the affected relations to avoid assigning evaluations to removed teams.`} + )} {satisfies ? ( { endpoint: `/relations`, requiresAuthorization: true, }); + const droppedTeamRelations = getRelationsWithDroppedTeams( + relationsResponse?.relations ?? [] + ); /** To fetch more projects when the bottom of the page is reached */ const observer = useRef(null); @@ -796,12 +800,21 @@ const AdministratorDashboard: NextPage = () => { } > {relationsResponse && relationsResponse.relations && ( - + + {droppedTeamRelations.length > 0 && ( + {`${ + droppedTeamRelations.length + } evaluation relation${ + droppedTeamRelations.length > 1 ? "s" : "" + } include removed teams. Delete or update the affected relations to avoid assigning evaluations to removed teams.`} + )} + + )} From 55aff74b49a10d2599594179026dc149dbb9f157 Mon Sep 17 00:00:00 2001 From: "sharonsohxuanhui@hotmail.com" Date: Fri, 26 Jun 2026 22:02:38 +0900 Subject: [PATCH 3/4] feat: highlight dropped teams in relation tables --- .../RelationRow/RelationByTeamRow.tsx | 38 +++++++---------- .../RelationCheckmarkRow.tsx | 23 ++++++---- .../RelationTable/RelationRow/RelationRow.tsx | 22 ++++++---- helpers/relations.test.ts | 42 ++++++++++++++++++- helpers/relations.ts | 9 ++++ 5 files changed, 97 insertions(+), 37 deletions(-) diff --git a/components/tables/RelationByTeamsTable/RelationRow/RelationByTeamRow.tsx b/components/tables/RelationByTeamsTable/RelationRow/RelationByTeamRow.tsx index 958c55a7..13dafa0a 100644 --- a/components/tables/RelationByTeamsTable/RelationRow/RelationByTeamRow.tsx +++ b/components/tables/RelationByTeamsTable/RelationRow/RelationByTeamRow.tsx @@ -1,7 +1,7 @@ import { FC } from "react"; // Components import HoverLink from "@/components/typography/HoverLink"; -import { TableCell, TableRow } from "@mui/material"; +import { Box, TableCell, TableRow } from "@mui/material"; // Helpers import { PAGES } from "@/helpers/navigation"; // Types @@ -33,6 +33,18 @@ const RelationByTeamRow: FC = ({ return null; } + const renderProjectLink = (project: Project) => ( + + + {project.teamName} + + + ); + return ( <> = ({ background: doesTeamFulfilRequirement ? "" : "#FFE4E4", }} > + {renderProjectLink(team)} - - {team.teamName} - - - - {evaluatees.map((evaluatee) => ( - - {evaluatee.teamName} - - ))} + {evaluatees.map((evaluatee) => renderProjectLink(evaluatee))} - {evaluators.map((evaluator) => ( - - {evaluator.teamName} - - ))} + {evaluators.map((evaluator) => renderProjectLink(evaluator))} {showAdviserColumn && ( diff --git a/components/tables/RelationCheckmarkTable/RelationCheckmarkRow/RelationCheckmarkRow.tsx b/components/tables/RelationCheckmarkTable/RelationCheckmarkRow/RelationCheckmarkRow.tsx index fcd76657..b0f8ba0a 100644 --- a/components/tables/RelationCheckmarkTable/RelationCheckmarkRow/RelationCheckmarkRow.tsx +++ b/components/tables/RelationCheckmarkTable/RelationCheckmarkRow/RelationCheckmarkRow.tsx @@ -1,11 +1,12 @@ import { Dispatch, FC, SetStateAction } from "react"; // Components import HoverLink from "@/components/typography/HoverLink"; -import { Checkbox, TableCell, TableRow } from "@mui/material"; +import { Box, Checkbox, TableCell, TableRow } from "@mui/material"; // Helpers import { PAGES } from "@/helpers/navigation"; // Types import { EvaluationRelation } from "@/types/relations"; +import { Project } from "@/types/projects"; type Props = { relation: EvaluationRelation; @@ -34,6 +35,18 @@ const RelationCheckmarkRow: FC = ({ }); }; + const renderProjectLink = (project: Project) => ( + + + {project.teamName} + + + ); + return ( <> @@ -46,14 +59,10 @@ const RelationCheckmarkRow: FC = ({ {relation.id} - - {relation.fromProject?.teamName} - + {relation.fromProject && renderProjectLink(relation.fromProject)} - - {relation.toProject?.teamName} - + {relation.toProject && renderProjectLink(relation.toProject)} {showAdviserColumn && ( diff --git a/components/tables/RelationTable/RelationRow/RelationRow.tsx b/components/tables/RelationTable/RelationRow/RelationRow.tsx index 7b0f1314..697b3e77 100644 --- a/components/tables/RelationTable/RelationRow/RelationRow.tsx +++ b/components/tables/RelationTable/RelationRow/RelationRow.tsx @@ -2,7 +2,7 @@ import { FC, useState } from "react"; // Components import HoverLink from "@/components/typography/HoverLink"; import DeleteRelationModal from "@/components/modals/DeleteRelationModal"; -import { Button, Stack, TableCell, TableRow } from "@mui/material"; +import { Box, Button, Stack, TableCell, TableRow } from "@mui/material"; import EditRelationModal from "@/components/modals/EditRelationModal"; // Helpers import { PAGES } from "@/helpers/navigation"; @@ -37,6 +37,18 @@ const RelationRow: FC = ({ setIsDeleteRelationOpen(true); }; + const renderProjectLink = (project: Project) => ( + + + {project.name} + + + ); + return ( <> = ({ {relation.id} - - {relation.fromProject?.name} - + {relation.fromProject && renderProjectLink(relation.fromProject)} - - {relation.toProject?.name} - + {relation.toProject && renderProjectLink(relation.toProject)} {showAdviserColumn && ( diff --git a/helpers/relations.test.ts b/helpers/relations.test.ts index b919ef33..1820c970 100644 --- a/helpers/relations.test.ts +++ b/helpers/relations.test.ts @@ -1,7 +1,11 @@ /* eslint-disable no-undef */ import { describe, expect } from "@jest/globals"; -import { generateRoundRobinRelations } from "./relations"; +import { + generateRoundRobinRelations, + getRelationsWithDroppedTeams, +} from "./relations"; import { LEVELS_OF_ACHIEVEMENT, Project } from "@/types/projects"; +import { EvaluationRelation } from "@/types/relations"; const createProject = (id: number, hasDropped = false): Project => ({ id, @@ -56,3 +60,39 @@ describe("#generateRoundRobinRelations", () => { ).toBe(false); }); }); + +describe("#getRelationsWithDroppedTeams", () => { + it("can find relations that include dropped teams", () => { + const activeProject = createProject(1); + const droppedProject = createProject(2, true); + const otherActiveProject = createProject(3); + + const relations = [ + { + id: 1, + fromProjectId: activeProject.id, + toProjectId: droppedProject.id, + fromProject: activeProject, + toProject: droppedProject, + }, + { + id: 2, + fromProjectId: activeProject.id, + toProjectId: otherActiveProject.id, + fromProject: activeProject, + toProject: otherActiveProject, + }, + { + id: 3, + fromProjectId: droppedProject.id, + toProjectId: otherActiveProject.id, + fromProject: droppedProject, + toProject: otherActiveProject, + }, + ] as EvaluationRelation[]; + + expect(getRelationsWithDroppedTeams(relations).map(({ id }) => id)).toEqual( + [1, 3] + ); + }); +}); diff --git a/helpers/relations.ts b/helpers/relations.ts index 6dd4c40e..a76e4647 100644 --- a/helpers/relations.ts +++ b/helpers/relations.ts @@ -70,6 +70,15 @@ export const generateGroupRelations = ( return relations; }; +export const getRelationsWithDroppedTeams = ( + relations: EvaluationRelation[] +) => { + return relations.filter( + (relation) => + relation.fromProject?.hasDropped || relation.toProject?.hasDropped + ); +}; + export const groupRelationsByTeam = (relations: EvaluationRelation[]) => { const groupedRelations: Record< number, From ebaf371827531ac34d12c8bc1d22ef9e0142af08 Mon Sep 17 00:00:00 2001 From: "sharonsohxuanhui@hotmail.com" Date: Fri, 26 Jun 2026 22:40:31 +0900 Subject: [PATCH 4/4] fix: remove block element inside --- .../RelationByTeamsTable/RelationRow/RelationByTeamRow.tsx | 1 - .../RelationCheckmarkRow/RelationCheckmarkRow.tsx | 1 - components/tables/RelationTable/RelationRow/RelationRow.tsx | 1 - 3 files changed, 3 deletions(-) diff --git a/components/tables/RelationByTeamsTable/RelationRow/RelationByTeamRow.tsx b/components/tables/RelationByTeamsTable/RelationRow/RelationByTeamRow.tsx index 13dafa0a..ca066a45 100644 --- a/components/tables/RelationByTeamsTable/RelationRow/RelationByTeamRow.tsx +++ b/components/tables/RelationByTeamsTable/RelationRow/RelationByTeamRow.tsx @@ -36,7 +36,6 @@ const RelationByTeamRow: FC = ({ const renderProjectLink = (project: Project) => ( diff --git a/components/tables/RelationCheckmarkTable/RelationCheckmarkRow/RelationCheckmarkRow.tsx b/components/tables/RelationCheckmarkTable/RelationCheckmarkRow/RelationCheckmarkRow.tsx index b0f8ba0a..cde24ab8 100644 --- a/components/tables/RelationCheckmarkTable/RelationCheckmarkRow/RelationCheckmarkRow.tsx +++ b/components/tables/RelationCheckmarkTable/RelationCheckmarkRow/RelationCheckmarkRow.tsx @@ -38,7 +38,6 @@ const RelationCheckmarkRow: FC = ({ const renderProjectLink = (project: Project) => ( diff --git a/components/tables/RelationTable/RelationRow/RelationRow.tsx b/components/tables/RelationTable/RelationRow/RelationRow.tsx index 697b3e77..f44e711c 100644 --- a/components/tables/RelationTable/RelationRow/RelationRow.tsx +++ b/components/tables/RelationTable/RelationRow/RelationRow.tsx @@ -40,7 +40,6 @@ const RelationRow: FC = ({ const renderProjectLink = (project: Project) => (