feat: add batch-add-results server intent with duplicate filtering

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris Parsons 2026-04-12 14:18:04 -07:00
parent a3c6e7ab09
commit 8723000cdd
3 changed files with 82 additions and 0 deletions

View file

@ -0,0 +1,29 @@
import { describe, it, expect } from "vitest";
import { filterNewResults } from "../admin.sports-seasons.$id.events.$eventId.helpers";
describe("filterNewResults", () => {
it("returns all rows when no existing results", () => {
const incoming = [
{ participantId: "p1", placement: 1 },
{ participantId: "p2", placement: 2 },
];
expect(filterNewResults(incoming, new Set())).toEqual(incoming);
});
it("skips participantIds already in existingIds", () => {
const incoming = [
{ participantId: "p1", placement: 1 },
{ participantId: "p2", placement: 2 },
];
const existing = new Set(["p1"]);
expect(filterNewResults(incoming, existing)).toEqual([
{ participantId: "p2", placement: 2 },
]);
});
it("returns empty array when all are duplicates", () => {
const incoming = [{ participantId: "p1", placement: 1 }];
const existing = new Set(["p1"]);
expect(filterNewResults(incoming, existing)).toEqual([]);
});
});

View file

@ -0,0 +1,10 @@
/**
* Filter incoming batch results to only those whose participantId
* is not already in the set of existing result participantIds.
*/
export function filterNewResults(
incoming: { participantId: string; placement: number }[],
existingParticipantIds: Set<string>
): { participantId: string; placement: number }[] {
return incoming.filter((r) => !existingParticipantIds.has(r.participantId));
}

View file

@ -10,11 +10,13 @@ import {
import {
getEventResults,
createEventResult,
createEventResultsBulk,
updateEventResult,
deleteEventResult,
type CreateEventResultData,
type UpdateEventResultData,
} from "~/models/event-result";
import { filterNewResults } from "./admin.sports-seasons.$id.events.$eventId.helpers";
import { findParticipantResultsBySportsSeasonId } from "~/models/participant-result";
import {
upsertParticipantSeasonResult,
@ -323,5 +325,46 @@ export async function action({ request, params }: Route.ActionArgs) {
}
}
if (intent === "batch-add-results") {
const resultsJson = formData.get("results");
if (typeof resultsJson !== "string" || !resultsJson) {
return { error: "Results data is required" };
}
let incoming: { participantId: string; placement: number }[];
try {
incoming = JSON.parse(resultsJson);
} catch {
return { error: "Invalid results format" };
}
if (!Array.isArray(incoming) || incoming.length === 0) {
return { error: "No results provided" };
}
try {
const existingResults = await getEventResults(params.eventId);
const existingIds = new Set(existingResults.map((r) => r.participantId));
const newResults = filterNewResults(incoming, existingIds);
if (newResults.length > 0) {
await createEventResultsBulk(
newResults.map((r) => ({
scoringEventId: params.eventId,
participantId: r.participantId,
placement: r.placement,
}))
);
}
return {
success: `${newResults.length} result${newResults.length === 1 ? "" : "s"} saved${incoming.length - newResults.length > 0 ? ` (${incoming.length - newResults.length} duplicate${incoming.length - newResults.length === 1 ? "" : "s"} skipped)` : ""}.`,
};
} catch (error) {
logger.error("Error saving batch results:", error);
return { error: "Failed to save results" };
}
}
return { error: "Invalid action" };
}