Fix First Four appearing out of order in NCAA bracket display
Use bracket template's canonical round order instead of sorting by match count, which placed First Four (4 matches) after Elite Eight (also 4 matches). Extract getOrderedRoundsFromMatches() into bracket-templates.ts to eliminate identical logic duplicated in the admin bracket component. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7f9b564a5d
commit
b4b4fc7e06
3 changed files with 33 additions and 20 deletions
|
|
@ -582,6 +582,32 @@ export function getAllBracketTemplates(): BracketTemplate[] {
|
||||||
return Object.values(BRACKET_TEMPLATES);
|
return Object.values(BRACKET_TEMPLATES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns round names in chronological order (earliest first) for the given matches.
|
||||||
|
* When a template is provided its defined order is used; otherwise rounds are sorted
|
||||||
|
* by descending match count (more matches = earlier round).
|
||||||
|
*/
|
||||||
|
export function getOrderedRoundsFromMatches(
|
||||||
|
matches: Array<{ round: string }>,
|
||||||
|
template?: BracketTemplate
|
||||||
|
): string[] {
|
||||||
|
const roundsInMatches = new Set(matches.map((m) => m.round));
|
||||||
|
|
||||||
|
if (template) {
|
||||||
|
return template.rounds
|
||||||
|
.map((r) => r.name)
|
||||||
|
.filter((name) => roundsInMatches.has(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
const roundMatchCounts = new Map<string, number>();
|
||||||
|
for (const m of matches) {
|
||||||
|
roundMatchCounts.set(m.round, (roundMatchCounts.get(m.round) || 0) + 1);
|
||||||
|
}
|
||||||
|
return Array.from(roundMatchCounts.keys()).sort(
|
||||||
|
(a, b) => (roundMatchCounts.get(b) || 0) - (roundMatchCounts.get(a) || 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to determine scoring round type based on match count in scoring rounds
|
* Helper to determine scoring round type based on match count in scoring rounds
|
||||||
* Used for determining placement sharing (5-8th, 3-4th, 1-2nd)
|
* Used for determining placement sharing (5-8th, 3-4th, 1-2nd)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import {
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "~/components/ui/select";
|
} from "~/components/ui/select";
|
||||||
import { ArrowLeft, Plus, Trophy, X, Check, ChevronDown, ChevronRight, Calendar, TrendingUp, Trash2 } from "lucide-react";
|
import { ArrowLeft, Plus, Trophy, X, Check, ChevronDown, ChevronRight, Calendar, TrendingUp, Trash2 } from "lucide-react";
|
||||||
import { getAllBracketTemplates, getBracketTemplate, buildNCAA68SlotMap, ALL_16_SEEDS, type BracketRegion } from "~/lib/bracket-templates";
|
import { getAllBracketTemplates, getBracketTemplate, getOrderedRoundsFromMatches, buildNCAA68SlotMap, ALL_16_SEEDS, type BracketRegion } from "~/lib/bracket-templates";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
|
@ -277,18 +277,8 @@ export default function EventBracket({
|
||||||
|
|
||||||
// Memoized: Get available rounds in chronological order
|
// Memoized: Get available rounds in chronological order
|
||||||
const availableRounds = useMemo(() => {
|
const availableRounds = useMemo(() => {
|
||||||
const roundsInMatches = Array.from(new Set(matches.map(m => m.round)));
|
const template = event.bracketTemplateId ? getBracketTemplate(event.bracketTemplateId) : undefined;
|
||||||
|
return getOrderedRoundsFromMatches(matches, template);
|
||||||
if (event.bracketTemplateId) {
|
|
||||||
const eventTemplate = getBracketTemplate(event.bracketTemplateId);
|
|
||||||
if (eventTemplate) {
|
|
||||||
return eventTemplate.rounds
|
|
||||||
.map((r) => r.name)
|
|
||||||
.filter((roundName: string) => roundsInMatches.includes(roundName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return roundsInMatches;
|
|
||||||
}, [matches, event.bracketTemplateId]);
|
}, [matches, event.bracketTemplateId]);
|
||||||
|
|
||||||
// Check if all matches are complete
|
// Check if all matches are complete
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import {
|
||||||
getUpcomingScoringEvents,
|
getUpcomingScoringEvents,
|
||||||
getRecentCompletedEvents,
|
getRecentCompletedEvents,
|
||||||
} from "~/models/scoring-event";
|
} from "~/models/scoring-event";
|
||||||
|
import { getBracketTemplate, getOrderedRoundsFromMatches } from "~/lib/bracket-templates";
|
||||||
import { database } from "~/database/context";
|
import { database } from "~/database/context";
|
||||||
import * as schema from "~/database/schema";
|
import * as schema from "~/database/schema";
|
||||||
import { eq, and, inArray } from "drizzle-orm";
|
import { eq, and, inArray } from "drizzle-orm";
|
||||||
|
|
@ -160,13 +161,9 @@ export async function loader(args: Route.LoaderArgs) {
|
||||||
});
|
});
|
||||||
playoffMatches = matches;
|
playoffMatches = matches;
|
||||||
|
|
||||||
const roundMatchCounts = new Map<string, number>();
|
const templateId = events.find((e) => e.bracketTemplateId)?.bracketTemplateId;
|
||||||
for (const m of matches) {
|
const template = templateId ? getBracketTemplate(templateId) : undefined;
|
||||||
roundMatchCounts.set(m.round, (roundMatchCounts.get(m.round) || 0) + 1);
|
playoffRounds = getOrderedRoundsFromMatches(matches, template);
|
||||||
}
|
|
||||||
playoffRounds = Array.from(roundMatchCounts.keys()).sort(
|
|
||||||
(a, b) => (roundMatchCounts.get(b) || 0) - (roundMatchCounts.get(a) || 0)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch group-stage losers and all participant results for bracket scoring
|
// Fetch group-stage losers and all participant results for bracket scoring
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue