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 @@ -75,7 +75,7 @@ export function parseSchemaToApiRequest(
const award = awardName as Award;

const isPersonal = (PERSONAL_AWARDS as readonly string[]).includes(award);
const isOptional = !(OPTIONAL_AWARDS as readonly string[]).includes(award);
const isOptional = (OPTIONAL_AWARDS as readonly string[]).includes(award);
const allowNominations = (CORE_VALUES_AWARDS as readonly string[]).includes(award);
const automaticAssignment = (AUTOMATIC_ASSIGNMENT_AWARDS as readonly string[]).includes(award);
const showPlaces = !(HIDE_PLACES as readonly string[]).includes(award);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { GraphQLFieldResolver } from 'graphql';
import { FinalDeliberationStage } from '@lems/database';
import { OPTIONAL_AWARDS } from '@lems/shared/awards';
import { RedisEventTypes } from '@lems/types/api/lems/redis';
import { MutationError, MutationErrorCode } from '@lems/types/api/lems';
import type { GraphQLContext } from '../../../apollo-server';
Expand Down Expand Up @@ -83,10 +82,8 @@ export const advanceFinalDeliberationStageResolver: GraphQLFieldResolver<

// Get awards for validation
const awards = await db.awards.byDivisionId(divisionId).getAll();
const hasOptionalAwards = awards.some(award =>
(OPTIONAL_AWARDS as readonly string[])
.filter(name => name !== 'excellence-in-engineering')
.includes(award.name)
const hasOptionalAwards = awards.some(
award => award.is_optional && award.name !== 'excellence-in-engineering'
);
if (nextStage === 'optional-awards' && !hasOptionalAwards) {
// Skip optional-awards stage if no optional awards exist
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { MutationError, MutationErrorCode } from '@lems/types/api/lems';
import { OPTIONAL_AWARDS } from '@lems/shared/awards';
import { Award, FinalDeliberationAwards } from '@lems/database';
import db from '../../../../../database';
import { updateFinalDeliberationAwards } from './utils';
Expand All @@ -13,9 +12,7 @@ export function validateOptionalAwardsStage(
): Promise<void> {
const divisionOptionalAwards = divisionAwards.filter(
award =>
(OPTIONAL_AWARDS as readonly string[])
.filter(name => name !== 'excellence-in-engineering')
.includes(award.name) && award.type === 'TEAM'
award.is_optional && award.name !== 'excellence-in-engineering' && award.type === 'TEAM'
);
for (const award of divisionOptionalAwards) {
if (!optionalAwards[award.name] || optionalAwards[award.name].length === 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GraphQLFieldResolver } from 'graphql';
import { RedisEventTypes } from '@lems/types/api/lems/redis';
import { MutationError, MutationErrorCode } from '@lems/types/api/lems';
import { Award, OPTIONAL_AWARDS } from '@lems/shared/awards';
import { Award } from '@lems/shared/awards';
import type { GraphQLContext } from '../../../apollo-server';
import db from '../../../../database';
import { getRedisPubSub } from '../../../../redis/redis-pubsub';
Expand Down Expand Up @@ -65,12 +65,18 @@ export const updateFinalDeliberationAwardsResolver: GraphQLFieldResolver<
);
}

// Fetch awards from database to check is_optional field
const divisionAwards = await db.awards.byDivisionId(divisionId).getAll();
const awardOptionalMap = new Map(divisionAwards.map(award => [award.name, award.is_optional]));

const optionalAwards: Partial<Record<Award, string[]>> = {};
const mandatoryAwards: Partial<Record<Award, string[]>> = {};
const championsAward: { '1'?: string; '2'?: string; '3'?: string; '4'?: string } = {};

Object.entries(awards).forEach(([awardName, awardData]) => {
if ((OPTIONAL_AWARDS as readonly string[]).includes(awardName)) {
const isOptional = awardOptionalMap.get(awardName) ?? false;

if (isOptional) {
if (awardName === 'champions') {
Object.assign(championsAward, awardData);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
} from '@mui/material';
import { useTranslations } from 'next-intl';
import { useMemo } from 'react';
import { OPTIONAL_AWARDS } from '@lems/shared';
import { ArrowBack } from '@mui/icons-material';
import { useRouter } from 'next/navigation';
import { useFinalDeliberation } from '../final-deliberation-context';
Expand All @@ -31,22 +30,20 @@ export const FinalDeliberationGrid: React.FC = () => {
const t = useTranslations('pages.deliberations.final');
const theme = useTheme();
const router = useRouter();
const { awardCounts, deliberation, anomalies } = useFinalDeliberation();
const { deliberation, anomalies, deliberationAwards } = useFinalDeliberation();

// Determine visible stages based on whether optional awards exist
const visibleStages = useMemo(() => {
if (!deliberation) {
return STAGES;
}

const hasOptionalAwards = Object.keys(awardCounts).some(award =>
(OPTIONAL_AWARDS as readonly string[])
.filter(name => name !== 'excellence-in-engineering')
.includes(award)
const hasOptionalAwards = deliberationAwards.some(
award => award.isOptional && award.name !== 'excellence-in-engineering'
);

return hasOptionalAwards ? STAGES : STAGES.filter(stage => stage !== 'optional-awards');
}, [awardCounts, deliberation]);
}, [deliberation, deliberationAwards]);

// Get current stage index
const currentStageIndex = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useCallback, useMemo } from 'react';
import { useTranslations } from 'next-intl';
import { Box, Paper, Stack, Typography, alpha, useTheme, IconButton, Tooltip } from '@mui/material';
import { Close } from '@mui/icons-material';
import { Award, OPTIONAL_AWARDS } from '@lems/shared';
import { Award } from '@lems/shared';
import { useAwardTranslations } from '@lems/localization';
import { useFinalDeliberation } from '../../final-deliberation-context';
import type { EnrichedTeam } from '../../types';
Expand Down Expand Up @@ -108,7 +108,7 @@ const AwardListItem: React.FC<AwardListItemProps> = ({ team, index, onRemove })
export const OptionalAwardsAwardLists: React.FC = () => {
const theme = useTheme();
const t = useTranslations('pages.deliberations.final.optional-awards');
const { teams, awards, awardCounts, updateAward } = useFinalDeliberation();
const { teams, awards, awardCounts, updateAward, deliberationAwards } = useFinalDeliberation();
const { getName } = useAwardTranslations();

const handleRemoveAward = useCallback(
Expand All @@ -123,10 +123,15 @@ export const OptionalAwardsAwardLists: React.FC = () => {
// Filter out excellence-in-engineering from optional awards
const displayedAwards = useMemo(
() =>
OPTIONAL_AWARDS.filter(
award => award !== 'excellence-in-engineering' && (awardCounts[award] ?? 0) > 0
) as Award[],
[awardCounts]
deliberationAwards
.filter(
award =>
award.isOptional &&
award.name !== 'excellence-in-engineering' &&
(awardCounts[award.name as Award] ?? 0) > 0
)
.map(award => award.name as Award),
[awardCounts, deliberationAwards]
);

const awardSelections = useMemo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Box, Button, LinearProgress, Stack, Typography, alpha, useTheme } from
import dayjs from 'dayjs';
import { useTranslations } from 'next-intl';
import { useCallback, useMemo, useState } from 'react';
import { OPTIONAL_AWARDS, Award, PERSONAL_AWARDS } from '@lems/shared';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filter personal awards from type

import { Award } from '@lems/shared';
import { Countdown } from '../../../../../../../../lib/time/countdown';
import { useTime } from '../../../../../../../../lib/time/hooks';
import { useFinalDeliberation } from '../../final-deliberation-context';
Expand All @@ -31,7 +31,7 @@ const getProgressColor = (progressPercent: number) => {
export const OptionalAwardsControlsPanel: React.FC = () => {
const theme = useTheme();
const t = useTranslations('pages.deliberations.final.optional-awards');
const { deliberation, startDeliberation, awards, awardCounts, advanceStage } =
const { deliberation, startDeliberation, awards, awardCounts, advanceStage, deliberationAwards } =
useFinalDeliberation();
const currentTime = useTime({ interval: 1000 });
const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -59,20 +59,24 @@ export const OptionalAwardsControlsPanel: React.FC = () => {
const isNotStarted = useMemo(() => !isInProgress && !isCompleted, [isInProgress, isCompleted]);

// Check if all optional awards are filled (or have their max limit)
// Filter out personal awards and excellence-in-engineering
const isOptionalAwardsComplete = useMemo(
() =>
deliberation
? OPTIONAL_AWARDS.filter(
award =>
award !== 'excellence-in-engineering' &&
!(PERSONAL_AWARDS as readonly string[]).includes(award)
).every(award => {
const selectedCount = getAwardArray(awards, award as Award).length;
const maxCount = awardCounts[award as Award] ?? 0;
return selectedCount === maxCount;
})
? deliberationAwards
.filter(
award =>
award.isOptional &&
award.name !== 'excellence-in-engineering' &&
award.type === 'TEAM'
)
.every(award => {
const selectedCount = getAwardArray(awards, award.name as Award).length;
const maxCount = awardCounts[award.name as Award] ?? 0;
return selectedCount === maxCount;
})
: false,
[deliberation, awards, awardCounts]
[deliberation, awards, awardCounts, deliberationAwards]
);

// Calculate timer values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from '@mui/material';
import { Stars, Add, CompareArrows } from '@mui/icons-material';
import { purple } from '@mui/material/colors';
import { OPTIONAL_AWARDS, Award } from '@lems/shared';
import { Award } from '@lems/shared';
import { useAwardTranslations } from '@lems/localization';
import { useFinalDeliberation } from '../../final-deliberation-context';
import type { EnrichedTeam } from '../../types';
Expand All @@ -29,8 +29,15 @@ export function OptionalAwardsDataGrid() {
const theme = useTheme();
const t = useTranslations('pages.deliberations.final.optional-awards');
const tTable = useTranslations('pages.deliberations.category.table');
const { teams, eligibleTeams, deliberation, awards, updateAward, awardCounts } =
useFinalDeliberation();
const {
teams,
eligibleTeams,
deliberation,
awards,
updateAward,
awardCounts,
deliberationAwards
} = useFinalDeliberation();
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const [selectedTeamForAward, setSelectedTeamForAward] = useState<string | null>(null);
const { getName } = useAwardTranslations();
Expand Down Expand Up @@ -72,13 +79,15 @@ export function OptionalAwardsDataGrid() {
// Get teams that have been selected for any optional award
const selectedTeamIds = useMemo<Set<string>>(() => {
const set = new Set<string>();
OPTIONAL_AWARDS.filter(award => award !== 'excellence-in-engineering').forEach(award => {
const awardArray = awards[award] as string[];
if (!awardArray) return;
awardArray.forEach(teamId => set.add(teamId));
});
deliberationAwards
.filter(award => award.isOptional && award.name !== 'excellence-in-engineering')
.forEach(award => {
const awardArray = awards[award.name as Award] as string[];
if (!awardArray) return;
awardArray.forEach(teamId => set.add(teamId));
});
return set;
}, [awards]);
}, [awards, deliberationAwards]);

const handleOpenPopover = useCallback(
(event: React.MouseEvent<HTMLButtonElement>, teamId: string) => {
Expand Down Expand Up @@ -290,10 +299,15 @@ export function OptionalAwardsDataGrid() {
// Get all available awards that can be assigned
const availableAwards: Award[] = useMemo(
() =>
OPTIONAL_AWARDS.filter(
award => award !== 'excellence-in-engineering' && (awardCounts[award] ?? 0) > 0
) as Award[],
[awardCounts]
deliberationAwards
.filter(
award =>
award.isOptional &&
award.name !== 'excellence-in-engineering' &&
(awardCounts[award.name as Award] ?? 0) > 0
)
.map(award => award.name as Award),
[awardCounts, deliberationAwards]
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ export const FinalDeliberationProvider = ({
categoryPicklists,
awards,
awardCounts,
deliberationAwards,
roomMetrics,
anomalies,
startDeliberation: handleStartFinalDeliberation,
Expand All @@ -261,6 +262,7 @@ export const FinalDeliberationProvider = ({
categoryPicklists,
awards,
awardCounts,
deliberationAwards,
handleStartFinalDeliberation,
handleUpdateFinalDeliberationAwards,
handleAdvanceStage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export interface FinalDeliberationContextValue {

awards: DeliberationAwards;
awardCounts: Partial<Record<Award, number>>;
deliberationAwards: Array<{ name: string; isOptional: boolean; type: string }>;

roomMetrics: RoomMetricsMap;

Expand Down