feat: replace numeric table assignment with dynamic two-floor lettere…#426
feat: replace numeric table assignment with dynamic two-floor lettere…#426jackzheng-cs wants to merge 2 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Updates CSV ingestion table assignment to generate two-floor letter-number table labels (e.g., A3) and prioritize “Best Hardware Hack” teams on floor 1, aligning with Issue #381’s new table labeling scheme.
Changes:
- Added a two-floor table assignment algorithm that distributes teams across lettered rows.
- Prioritized hardware teams on floor 1 and filled remaining floor 1 capacity with non-hardware teams.
- Replaced the previous sequential numeric table numbering assignment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // 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++; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 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 | ||
| const floor1Rows = ALL_ROWS.slice(0, floor1RowCount); | ||
| const floor2Rows = ALL_ROWS.slice( | ||
| floor1RowCount, | ||
| floor1RowCount + floor2RowCount | ||
| ); |
There was a problem hiding this comment.
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.
| 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++; |
There was a problem hiding this comment.
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.
| Math.ceil(floor2Teams.length / FLOOR2_MAX_PER_ROW) | ||
| ); | ||
|
|
||
| // Deligate table letters based on row counts for each floor |
There was a problem hiding this comment.
Minor typos in comments: extra ")" in "(Prioritize hardware teams))" and "Deligate" should be "Delegate". Please fix to avoid confusion when maintaining this logic.
| // Deligate table letters based on row counts for each floor | |
| // Delegate table letters based on row counts for each floor |
Summary