Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -80,6 +81,7 @@ const AdviserManageRelationsContent: FC<Props> = ({

const { teamsThatDoNotSatisfy, satisfies } =
checkIfAllTeamsSatisfyRequirements();
const droppedTeamRelations = getRelationsWithDroppedTeams(relations);

return (
<>
Expand All @@ -88,6 +90,13 @@ const AdviserManageRelationsContent: FC<Props> = ({
Summary
</Typography>
<Typography>{`You are currently assigned to ${projects.length} teams`}</Typography>
{droppedTeamRelations.length > 0 && (
<Typography fontWeight="bold" color="red">{`${
droppedTeamRelations.length
} evaluation relation${
droppedTeamRelations.length > 1 ? "s" : ""
} include removed teams. Delete or update the affected relations to avoid assigning evaluations to removed teams.`}</Typography>
)}
{satisfies ? (
<Typography
fontWeight="bold"
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -33,37 +33,30 @@ const RelationByTeamRow: FC<Props> = ({
return null;
}

const renderProjectLink = (project: Project) => (
<Box
key={project.id}
sx={{ color: project.hasDropped ? "red" : "inherit" }}
>
<HoverLink href={`${PAGES.PROJECTS}/${project.id}`}>
{project.teamName}
</HoverLink>
</Box>
);
Comment thread
Copilot marked this conversation as resolved.

return (
<>
<TableRow
sx={{
background: doesTeamFulfilRequirement ? "" : "#FFE4E4",
}}
>
<TableCell>{renderProjectLink(team)}</TableCell>
<TableCell>
<HoverLink href={`${PAGES.PROJECTS}/${team.id}`}>
{team.teamName}
</HoverLink>
</TableCell>
<TableCell>
{evaluatees.map((evaluatee) => (
<HoverLink
key={evaluatee.id}
href={`${PAGES.PROJECTS}/${evaluatee.id}`}
>
{evaluatee.teamName}
</HoverLink>
))}
{evaluatees.map((evaluatee) => renderProjectLink(evaluatee))}
</TableCell>
<TableCell>
{evaluators.map((evaluator) => (
<HoverLink
key={evaluator.id}
href={`${PAGES.PROJECTS}/${evaluator.id}`}
>
{evaluator.teamName}
</HoverLink>
))}
{evaluators.map((evaluator) => renderProjectLink(evaluator))}
</TableCell>
{showAdviserColumn && (
<TableCell>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -34,6 +35,17 @@ const RelationCheckmarkRow: FC<Props> = ({
});
};

const renderProjectLink = (project: Project) => (
<Box
key={project.id}
sx={{ color: project.hasDropped ? "red" : "inherit" }}
>
<HoverLink href={`${PAGES.PROJECTS}/${project.id}`}>
{project.teamName}
</HoverLink>
</Box>
);
Comment thread
xGladiate marked this conversation as resolved.

return (
<>
<TableRow>
Expand All @@ -46,14 +58,10 @@ const RelationCheckmarkRow: FC<Props> = ({
</TableCell>
<TableCell>{relation.id}</TableCell>
<TableCell>
<HoverLink href={`${PAGES.PROJECTS}/${relation.fromProjectId}`}>
{relation.fromProject?.teamName}
</HoverLink>
{relation.fromProject && renderProjectLink(relation.fromProject)}
</TableCell>
<TableCell>
<HoverLink href={`${PAGES.PROJECTS}/${relation.toProjectId}`}>
{relation.toProject?.teamName}
</HoverLink>
{relation.toProject && renderProjectLink(relation.toProject)}
</TableCell>
{showAdviserColumn && (
<TableCell>
Expand Down
21 changes: 14 additions & 7 deletions components/tables/RelationTable/RelationRow/RelationRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -37,6 +37,17 @@ const RelationRow: FC<Props> = ({
setIsDeleteRelationOpen(true);
};

const renderProjectLink = (project: Project) => (
<Box
key={project.id}
sx={{ color: project.hasDropped ? "red" : "inherit" }}
>
<HoverLink href={`${PAGES.PROJECTS}/${project.id}`}>
{project.name}
</HoverLink>
</Box>
);
Comment thread
Copilot marked this conversation as resolved.

return (
<>
<EditRelationModal
Expand All @@ -55,14 +66,10 @@ const RelationRow: FC<Props> = ({
<TableRow>
<TableCell>{relation.id}</TableCell>
<TableCell>
<HoverLink href={`${PAGES.PROJECTS}/${relation.fromProjectId}`}>
{relation.fromProject?.name}
</HoverLink>
{relation.fromProject && renderProjectLink(relation.fromProject)}
</TableCell>
<TableCell>
<HoverLink href={`${PAGES.PROJECTS}/${relation.toProjectId}`}>
{relation.toProject?.name}
</HoverLink>
{relation.toProject && renderProjectLink(relation.toProject)}
</TableCell>
{showAdviserColumn && (
<TableCell>
Expand Down
98 changes: 98 additions & 0 deletions helpers/relations.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* eslint-disable no-undef */
import { describe, expect } from "@jest/globals";
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,
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);
});
});

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]
);
});
});
16 changes: 13 additions & 3 deletions helpers/relations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const generateRoundRobinRelations = (
projects: Project[]
): Partial<EvaluationRelation>[] => {
const relations: Partial<EvaluationRelation>[] = [];
const numberOfProjects = projects.length;
const activeProjects = projects.filter((project) => !project.hasDropped);
const numberOfProjects = activeProjects.length;

const numberOfEvaluations = Math.min(
numberOfProjects - 1,
Expand All @@ -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,
Expand Down Expand Up @@ -69,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,
Expand Down
25 changes: 19 additions & 6 deletions pages/dashboard/administrator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import useInfiniteFetch, {
} from "@/hooks/useInfiniteFetch";
import { getTabFromQuery, transformTabNameIntoId } from "@/helpers/dashboard";
import { Cohort } from "@/types/cohorts";
import { getRelationsWithDroppedTeams } from "@/helpers/relations";
import { useRouter } from "next/router";

enum TAB {
Expand Down Expand Up @@ -338,6 +339,9 @@ const AdministratorDashboard: NextPage = () => {
endpoint: `/relations`,
requiresAuthorization: true,
});
const droppedTeamRelations = getRelationsWithDroppedTeams(
relationsResponse?.relations ?? []
);

/** To fetch more projects when the bottom of the page is reached */
const observer = useRef<IntersectionObserver | null>(null);
Expand Down Expand Up @@ -796,12 +800,21 @@ const AdministratorDashboard: NextPage = () => {
}
>
{relationsResponse && relationsResponse.relations && (
<RelationTable
relations={relationsResponse.relations}
mutate={mutateRelations}
projects={projectsResponse?.projects ?? []}
showAdviserColumn
/>
<Stack gap="1rem">
{droppedTeamRelations.length > 0 && (
<Typography fontWeight="bold" color="red">{`${
droppedTeamRelations.length
} evaluation relation${
droppedTeamRelations.length > 1 ? "s" : ""
} include removed teams. Delete or update the affected relations to avoid assigning evaluations to removed teams.`}</Typography>
)}
<RelationTable
relations={relationsResponse.relations}
mutate={mutateRelations}
projects={projectsResponse?.projects ?? []}
showAdviserColumn
/>
</Stack>
)}
</NoDataWrapper>
</Stack>
Expand Down
Loading