Skip to content
Open
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 @@ -128,7 +128,18 @@ export function DeliberationTable() {
align: 'center' as const,
renderCell: params => {
const value = params.row.rubricFields[label];
return value !== null ? value : '-';
const notes = params.row.rubricFieldNotes[label];
const hasExceedsNote = value === 4 && notes;

if (value === null) return '-';

return hasExceedsNote ? (
<Tooltip title={notes} arrow placement="top">
<Box sx={{ cursor: 'help' }}>{value}</Box>
</Tooltip>
) : (
value
);
}
}) as GridColDef<EnrichedTeam>
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
getFieldDisplayLabels,
computeNormalizedScores,
getOrganizedRubricFields,
getOrganizedRubricFieldNotes,
getGPScores
} from '../utils';
import type { DeliberationContextValue, EnrichedTeam } from './types';
Expand Down Expand Up @@ -126,6 +127,7 @@ export function CategoryDeliberationProvider({
const rank = computeRank(scores, teamScores, category);
const isEligible = computeEligibility(team, deliberation);
const rubricFields = getOrganizedRubricFields(team, category);
const rubricFieldNotes = getOrganizedRubricFieldNotes(team, category);
const gpScores = getGPScores(team);

return {
Expand All @@ -141,6 +143,7 @@ export function CategoryDeliberationProvider({
rank,
eligible: isEligible,
rubricFields,
rubricFieldNotes,
gpScores,
rubricId: team.rubrics[hypenatedCategory as keyof typeof team.rubrics]?.id ?? null,
awardNominations: team.rubrics.core_values?.data?.awards ?? {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface EnrichedTeam {
eligible: boolean;

rubricFields: Record<string, number | null>;
rubricFieldNotes: Record<string, string | undefined>;
gpScores: Record<string, number | null>;

rubricId: string | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface SectionFields {
fieldId: string;
value: number | null;
color: 'success' | 'error' | 'default';
notes?: string;
}>;
}

Expand Down Expand Up @@ -54,7 +55,8 @@ const processFieldsBySections = (
fieldId: id,
category: 'innovation-project' as const,
value: field.value,
color: getFieldComparisonColor(id, team.id, fieldComparisons, 'ip')
color: getFieldComparisonColor(id, team.id, fieldComparisons, 'ip'),
notes: field.notes
})),
...Object.entries(team.rubrics.robot_design?.data?.fields || {})
.filter(([id]) =>
Expand All @@ -66,7 +68,8 @@ const processFieldsBySections = (
fieldId: id,
category: 'robot-design' as const,
value: field.value,
color: getFieldComparisonColor(id, team.id, fieldComparisons, 'rd')
color: getFieldComparisonColor(id, team.id, fieldComparisons, 'rd'),
notes: field.notes
}))
];
if (fields.length > 0) result[cat] = { 'core-values': fields };
Expand All @@ -83,7 +86,8 @@ const processFieldsBySections = (
fieldId: field.id,
category: cat,
value: rubricData.fields[field.id].value,
color: getFieldComparisonColor(field.id, team.id, fieldComparisons)
color: getFieldComparisonColor(field.id, team.id, fieldComparisons),
notes: rubricData.fields[field.id].notes
}));
if (sectionFields.length > 0) sections[section.id] = sectionFields;
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { Stack, Typography, Chip } from '@mui/material';
import { Stack, Typography, Chip, Tooltip } from '@mui/material';
import { useRubricsTranslations } from '@lems/localization';
import { JudgingCategory } from '@lems/types/judging';

interface SectionScoreRowProps {
category: JudgingCategory;
sectionId: string;
scores: Array<{ fieldId: string; value: number | null; color: 'success' | 'error' | 'default' }>;
scores: Array<{
fieldId: string;
value: number | null;
color: 'success' | 'error' | 'default';
notes?: string;
}>;
showSectionName?: boolean;
showAllScores?: boolean;
}
Expand Down Expand Up @@ -45,25 +50,37 @@ export function SectionScoreRow({
alignItems: 'center'
}}
>
{(showAllScores ? scores : scores.slice(0, 2)).map((score, index) => (
<Chip
key={index}
label={score.value ?? 'N/A'}
size={showAllScores ? 'medium' : 'small'}
color={score.color}
sx={{
minWidth: showAllScores ? 48 : 40,
height: showAllScores ? 32 : 24,
fontSize: showAllScores ? '0.9rem' : '0.8rem',
fontWeight: 600,
margin: showAllScores ? '2px' : '0px',
'& .MuiChip-label': {
px: showAllScores ? 1 : 0.75,
fontSize: showAllScores ? '0.9rem' : '0.8rem'
}
}}
/>
))}
{(showAllScores ? scores : scores.slice(0, 2)).map((score, index) => {
const hasExceedsNote = score.value === 4 && score.notes;

return (
<Tooltip
key={index}
title={hasExceedsNote ? score.notes : ''}
arrow
placement="top"
disableHoverListener={!hasExceedsNote}
>
<Chip
label={score.value ?? 'N/A'}
size={showAllScores ? 'medium' : 'small'}
color={score.color}
sx={{
minWidth: showAllScores ? 48 : 40,
height: showAllScores ? 32 : 24,
fontSize: showAllScores ? '0.9rem' : '0.8rem',
fontWeight: 600,
margin: showAllScores ? '2px' : '0px',
cursor: hasExceedsNote ? 'help' : 'default',
'& .MuiChip-label': {
px: showAllScores ? 1 : 0.75,
fontSize: showAllScores ? '0.9rem' : '0.8rem'
}
}}
/>
</Tooltip>
);
})}
</Stack>
</Stack>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,18 @@ export function OptionalAwardsDataGrid() {
align: 'center' as const,
renderCell: params => {
const value = params.row.rubricsFields['core-values'][label];
return value !== null ? value : '-';
const notes = params.row.rubricsFieldNotes['core-values'][label];
const hasExceedsNote = value === 4 && notes;

if (value === null) return '-';

return hasExceedsNote ? (
<Tooltip title={notes} arrow placement="top">
<Box sx={{ cursor: 'help' }}>{value}</Box>
</Tooltip>
) : (
value
);
}
}) as GridColDef<EnrichedTeam>
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
computeRoomMetrics,
computeTeamScores,
getGPScores,
getOrganizedRubricFields
getOrganizedRubricFields,
getOrganizedRubricFieldNotes
} from '../utils';
import {
DeliberationAwards,
Expand Down Expand Up @@ -209,6 +210,11 @@ export const FinalDeliberationProvider = ({
'innovation-project': getOrganizedRubricFields(team, 'innovation-project'),
'core-values': getOrganizedRubricFields(team, 'core-values')
},
rubricsFieldNotes: {
'robot-design': getOrganizedRubricFieldNotes(team, 'robot-design'),
'innovation-project': getOrganizedRubricFieldNotes(team, 'innovation-project'),
'core-values': getOrganizedRubricFieldNotes(team, 'core-values')
},
rubricIds: {
'robot-design': team.rubrics.robot_design?.id || null,
'innovation-project': team.rubrics.innovation_project?.id || null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export type OptionalAwardNominations = Partial<Record<OptionalAwards, boolean>>;
export type RanksPerCategory = Record<JudgingCategory | 'total' | 'robot-game', number>;
export type EligiblityPerStage = Record<StagesWithNomination, boolean>;
export type RubricFields = Record<string, number | null>;
export type RubricFieldNotes = Record<string, string | undefined>;
export type RubricsFields = Record<JudgingCategory, RubricFields>;
export type RubricsFieldNotes = Record<JudgingCategory, RubricFieldNotes>;

/**
* Enriched team data with all computed values for final deliberation.
Expand All @@ -33,6 +35,7 @@ export type EnrichedTeam = {
eligibility: EligiblityPerStage;

rubricsFields: RubricsFields;
rubricsFieldNotes: RubricsFieldNotes;
gpScores: Record<string, number | null>;

rubricIds: Record<JudgingCategory, string | null>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,62 @@ export function buildFieldMetadata(category: JudgingCategory): FieldMetadata[] {
return fields;
}

/**
* Extracts organized field notes for a team in a specific category.
*
* Returns field notes mapped by their display labels, ordered by field number.
* For core-values category, includes both IP and RD fields with their respective prefixes.
*
* @param team - The team to extract field notes from
* @param category - The deliberation category (hyphenated)
* @returns Object mapping display labels (e.g., 'IP-1') to field notes
*/
export function getOrganizedRubricFieldNotes(
team: Team,
category: JudgingCategory
): Record<string, string | undefined> {
const fields = buildFieldMetadata(category);
const result: Record<string, string | undefined> = {};

const categoryKey = hyphensToUnderscores(category) as
| 'innovation_project'
| 'robot_design'
| 'core_values';

const categoryMap = {
innovation_project: team.rubrics.innovation_project,
robot_design: team.rubrics.robot_design,
core_values: team.rubrics.core_values
} as const;

if (categoryKey === 'core_values') {
// For core-values, include both IP and RD fields with prefixes
const ipFields = buildFieldMetadata('innovation-project').filter(f => f.coreValues);
const rdFields = buildFieldMetadata('robot-design').filter(f => f.coreValues);

ipFields.forEach(field => {
const ipRubric = team.rubrics.innovation_project;
const notes = ipRubric?.data?.fields?.[field.id]?.notes;
result[field.displayLabel] = notes;
});

rdFields.forEach(field => {
const rdRubric = team.rubrics.robot_design;
const notes = rdRubric?.data?.fields?.[field.id]?.notes;
result[field.displayLabel] = notes;
});
} else {
// For IP/RD, include only their own fields
fields.forEach(field => {
const rubric = categoryMap[categoryKey];
const notes = rubric?.data?.fields?.[field.id]?.notes;
result[field.displayLabel] = notes;
});
}

return result;
}

/**
* Extracts GP scores with formatted keys for a team.
*
Expand Down