Refactor playoff event processing and improve code clarity (#261)

* Remove redundant processPlayoffEvent loop from finalize-bracket action

All bracket rounds are already processed (with scoring and Discord
notifications) as each match winner is set via set-winner/set-round-winners.
By the time the Finalize button is clicked, placements are current and
standings are up to date. The re-processing loop was firing recalculations
and Discord notifications once per round needlessly.

The finalize action now only assigns 0 points to non-bracket participants,
marks the event complete, and runs one final standings recalculation.

https://claude.ai/code/session_01RhQS6FQRh6iYtVNaryCEf5

* Fix stale comment in finalize-bracket action

The template is now fetched only as a validity guard, not for round
order iteration (which was removed in the previous commit).

https://claude.ai/code/session_01RhQS6FQRh6iYtVNaryCEf5

* Fix complete-round redundant Discord/recalculation and stale comment

complete-round was calling processPlayoffEvent without skipRecalculate,
firing recalculateAffectedLeagues and Discord even though each match
winner had already triggered those side effects via set-winner. Add
skipRecalculate: true to match the autoCompleteRoundIfDone pattern.

Also fix autoCompleteRoundIfDone comment which incorrectly said
"non-bracket eliminations are recorded" — that's a separate step in
finalize-bracket; processPlayoffEvent records bracket round placements.

https://claude.ai/code/session_01RhQS6FQRh6iYtVNaryCEf5

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Chris Parsons 2026-04-05 15:11:43 -07:00 committed by GitHub
parent a71e256bfd
commit fbeee4ed15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -212,7 +212,7 @@ export async function action({ request, params }: Route.ActionArgs) {
/**
* Auto-complete a round when every match in it is marked complete.
* Updates scoringEvents.playoffRound and calls processPlayoffEvent so that
* non-bracket eliminations are recorded and probabilities are refreshed.
* round placements are recorded and probabilities are refreshed.
* This replaces the manual "Complete Round" button.
*
* skipRecalculate=true because the caller already sent a Discord notification
@ -552,8 +552,10 @@ export async function action({ request, params }: Route.ActionArgs) {
};
}
// Process playoff event to update participant results
await processPlayoffEvent(params.eventId);
// Process playoff event to update participant results.
// skipRecalculate=true: match winners were already set via set-winner/set-round-winners,
// which triggered recalculateAffectedLeagues and Discord per match. No need to re-fire.
await processPlayoffEvent(params.eventId, undefined, { skipRecalculate: true });
return {
success: `${round} completed and placements calculated successfully`,
@ -690,7 +692,7 @@ export async function action({ request, params }: Route.ActionArgs) {
return { error: "No bracket exists for this event" };
}
// Get template to determine round order
// Verify the event has a valid bracket template configured
const template = event.bracketTemplateId ? getBracketTemplate(event.bracketTemplateId) : null;
if (!template) {
return { error: "Bracket template not found" };
@ -712,20 +714,7 @@ export async function action({ request, params }: Route.ActionArgs) {
matches.flatMap((m) => [m.participant1Id, m.participant2Id].filter(Boolean))
);
// Process all rounds in order
const db = database();
for (const round of template.rounds) {
const roundMatches = matches.filter((m) => m.round === round.name);
if (roundMatches.length === 0) continue;
// Set playoffRound and process this round
await db
.update(schema.scoringEvents)
.set({ playoffRound: round.name, updatedAt: new Date() })
.where(eq(schema.scoringEvents.id, params.eventId));
await processPlayoffEvent(params.eventId, db);
}
// Assign 0 points to participants not in the bracket (Q20)
for (const participant of allParticipants) {