Skip to content
Open
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
86 changes: 80 additions & 6 deletions app/(api)/_utils/csv-ingestion/csvAlgorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,83 @@ export function sortTracks(
return ordered;
}

// Table number assignment
const ALL_ROWS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');

// Max teams per row on Floor 1 (Prioritize hardware teams))
const FLOOR1_MAX_PER_ROW = 5;

// Max teams per row on Floor 2
const FLOOR2_MAX_PER_ROW = 10;

/**
* Spreads teams evenly across rows, filling earlier rows first when there is a remainder
*
* Each team's tableNumber is set to e.g. "A3", "B1"
*/

function distributeAcrossRows(teams: ParsedRecord[], rows: string[]): void {
if (teams.length === 0 || rows.length === 0) return;
const baseCount = Math.floor(teams.length / rows.length);
const remainder = teams.length % rows.length;

let i = 0;
for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
const letter = rows[rowIdx];
const rowSize = baseCount + (rowIdx < remainder ? 1 : 0);
for (let seat = 1; seat <= rowSize; seat++) {
teams[i].tableNumber = `${letter}${seat}` as any;
i++;
Comment on lines +353 to +358
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

teams[i].tableNumber = ${letter}${seat} as any; bypasses typing, but ParsedRecord.tableNumber (and the persisted Team.tableNumber) are currently typed/used as number throughout the codebase. Persisting a string like "A3" will break consumers that compare or query by numeric tableNumber. Prefer updating the relevant types and downstream consumers (API queries, floor calculations, UI) to accept the new string format, or introduce a separate field (e.g. tableLabel) while keeping tableNumber numeric.

Copilot uses AI. Check for mistakes.
}
}
}

/**
* Assigns two-floor lettered table numbers:
*
* Floor 1 — Hardware Hack teams first, with leftover seats filled by other teams in .csv order.
*
* Floor 2 — remaining other teams after floor 1 spillover is accounted for.
* Rows start immediately after floor 1's last letter.
*/
function assignTableNumbers(
hardwareTeams: ParsedRecord[],
otherTeams: ParsedRecord[]
): void {
// Total row count for floor 1 for hardware teams
const floor1RowCount = Math.max(
1,
Math.ceil(hardwareTeams.length / FLOOR1_MAX_PER_ROW)
);

// How many seats are available on floor 1 vs how many hardware teams fill them.
const floor1Capacity = floor1RowCount * FLOOR1_MAX_PER_ROW;
const floor1Spillover = floor1Capacity - hardwareTeams.length;

// Pull enough other teams to fill the leftover floor 1 seats.
const floor1OtherTeams = otherTeams.slice(0, floor1Spillover);
const floor2Teams = otherTeams.slice(floor1Spillover);

const floor1Teams = [...hardwareTeams, ...floor1OtherTeams];

// Total row count for floor 2
const floor2RowCount = Math.max(
1,
Math.ceil(floor2Teams.length / FLOOR2_MAX_PER_ROW)
);

// Deligate table letters based on row counts for each floor
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

Minor typos in comments: extra ")" in "(Prioritize hardware teams))" and "Deligate" should be "Delegate". Please fix to avoid confusion when maintaining this logic.

Suggested change
// Deligate table letters based on row counts for each floor
// Delegate table letters based on row counts for each floor

Copilot uses AI. Check for mistakes.
const floor1Rows = ALL_ROWS.slice(0, floor1RowCount);
const floor2Rows = ALL_ROWS.slice(
floor1RowCount,
floor1RowCount + floor2RowCount
);
Comment on lines +332 to +402
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

ALL_ROWS is limited to A–Z. If floor1RowCount or floor1RowCount + floor2RowCount exceeds 26, slice(...) will return fewer/zero rows, causing distributeAcrossRows to assign too many seats per letter and/or leave some teams without a tableNumber (since rows.length === 0 early-returns). Add an explicit guard with a clear error when required rows exceed available labels, or implement multi-letter labels (AA, AB, …) so all teams get assigned deterministically.

Copilot uses AI. Check for mistakes.

// Distribute teams across each floor's rows
distributeAcrossRows(floor1Teams, floor1Rows);
distributeAcrossRows(floor2Teams, floor2Rows);
}

export async function validateCsvBlob(blob: Blob): Promise<{
ok: boolean;
body: ParsedRecord[] | null;
Expand Down Expand Up @@ -473,13 +550,10 @@ export async function validateCsvBlob(blob: Blob): Promise<{
(team) => !team.tracks.includes('Best Hardware Hack')
);

const orderedTeams = [...bestHardwareTeams, ...otherTeams];

orderedTeams.forEach((team, index) => {
team.tableNumber = index + 1;
});
assignTableNumbers(bestHardwareTeams, otherTeams);

resolve(orderedTeams);
// Hardware teams first in the returned list for display ordering.
resolve([...bestHardwareTeams, ...otherTeams]);
})
.on('error', (error) => reject(error));
};
Expand Down
Loading