diff --git a/app/routes/admin.sports-seasons.$id.events.$eventId.server.ts b/app/routes/admin.sports-seasons.$id.events.$eventId.server.ts index c1a997b..523877c 100644 --- a/app/routes/admin.sports-seasons.$id.events.$eventId.server.ts +++ b/app/routes/admin.sports-seasons.$id.events.$eventId.server.ts @@ -27,6 +27,8 @@ import { getQPStandings, getQPConfig } from "~/models/qualifying-points"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { eq } from "drizzle-orm"; +import { upsertTournamentResult } from "~/models/tournament-result"; +import { syncTournamentResults } from "~/services/sync-tournament-results"; export async function loader({ params }: Route.LoaderArgs) { const sportsSeason = await findSportsSeasonById(params.id); @@ -372,6 +374,46 @@ export async function action({ request, params }: Route.ActionArgs) { } try { + // If this scoring event is linked to a canonical tournament, write + // results to the canonical layer and fan out via syncTournamentResults. + // This keeps sibling windows (if any) in sync automatically. + const scoringEvent = await database().query.scoringEvents.findFirst({ + where: eq(schema.scoringEvents.id, params.eventId), + }); + if (!scoringEvent) { + return { error: "Scoring event not found" }; + } + + if (scoringEvent.tournamentId) { + // Canonical path: translate season_participant.id → canonical participant.id + const spRows = allSeasonParticipants.filter((sp) => + incoming.some((r) => r.participantId === sp.id), + ); + const missingCanonical = spRows.filter((sp) => !sp.participantId); + if (missingCanonical.length > 0) { + return { + error: `Some season participants are not linked to canonical participants: ${missingCanonical.map((sp) => sp.name).join(", ")}`, + }; + } + + for (const row of incoming) { + const sp = spRows.find((s) => s.id === row.participantId); + if (!sp?.participantId) continue; + await upsertTournamentResult({ + tournamentId: scoringEvent.tournamentId, + participantId: sp.participantId, + placement: row.placement, + }); + } + + const syncReport = await syncTournamentResults(scoringEvent.tournamentId); + return { + success: `${incoming.length} result${incoming.length === 1 ? "" : "s"} saved canonically. Synced to ${syncReport.windowsSynced} window${syncReport.windowsSynced === 1 ? "" : "s"}${syncReport.windowsFailed > 0 ? ` (${syncReport.windowsFailed} failed)` : ""}.`, + syncReport, + }; + } + + // Legacy direct-write path for non-canonical events (team sports, etc.). const existingResults = await getEventResults(params.eventId); const existingIds = new Set(existingResults.map((r) => r.seasonParticipantId)); const newResults = filterNewResults(incoming, existingIds);