fix: address final review issues (sportsSeasonId authority, participant validation, CRLF, unused prop)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
057f935c88
commit
57cc66ca36
5 changed files with 22 additions and 11 deletions
|
|
@ -44,14 +44,12 @@ type Stage = "idle" | "preview" | "saving";
|
|||
|
||||
interface BatchResultEntryProps {
|
||||
participants: Participant[];
|
||||
eventId: string;
|
||||
sportsSeasonId: string;
|
||||
existingResultParticipantIds: Set<string>;
|
||||
}
|
||||
|
||||
export function BatchResultEntry({
|
||||
participants,
|
||||
eventId: _eventId,
|
||||
sportsSeasonId,
|
||||
existingResultParticipantIds,
|
||||
}: BatchResultEntryProps) {
|
||||
|
|
@ -165,7 +163,7 @@ export function BatchResultEntry({
|
|||
}
|
||||
|
||||
function handleConfirm() {
|
||||
if (!allResolved) return;
|
||||
if (!allResolved || rows.length === 0) return;
|
||||
setStage("saving");
|
||||
const results = rows
|
||||
.filter((r) => r.participantId !== null)
|
||||
|
|
|
|||
|
|
@ -78,6 +78,14 @@ describe("parseResultsText", () => {
|
|||
{ placement: 1, rawName: "Gerwyn Price" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("handles \\r\\n line endings", () => {
|
||||
const input = "1. Gerwyn Price\r\n2. Luke Littler";
|
||||
expect(parseResultsText(input)).toEqual([
|
||||
{ placement: 1, rawName: "Gerwyn Price" },
|
||||
{ placement: 2, rawName: "Luke Littler" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("multi-line mixed formats", () => {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export interface ParsedLine {
|
|||
export function parseResultsText(text: string): ParsedLine[] {
|
||||
const results: ParsedLine[] = [];
|
||||
|
||||
for (const rawLine of text.split("\n")) {
|
||||
for (const rawLine of text.split(/\r?\n/)) {
|
||||
const line = rawLine.trim();
|
||||
if (!line) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -363,6 +363,14 @@ export async function action({ request, params }: Route.ActionArgs) {
|
|||
return { error: "No results provided" };
|
||||
}
|
||||
|
||||
// Validate that all participant IDs belong to this sports season
|
||||
const allSeasonParticipants = await findParticipantsBySportsSeasonId(params.id);
|
||||
const validParticipantIds = new Set(allSeasonParticipants.map((p) => p.id));
|
||||
const invalidEntries = incoming.filter((r) => !validParticipantIds.has(r.participantId));
|
||||
if (invalidEntries.length > 0) {
|
||||
return { error: "Some participant IDs do not belong to this sports season" };
|
||||
}
|
||||
|
||||
try {
|
||||
const existingResults = await getEventResults(params.eventId);
|
||||
const existingIds = new Set(existingResults.map((r) => r.participantId));
|
||||
|
|
@ -389,20 +397,15 @@ export async function action({ request, params }: Route.ActionArgs) {
|
|||
|
||||
if (intent === "create-participant") {
|
||||
const name = formData.get("name");
|
||||
const sportsSeasonId = formData.get("sportsSeasonId");
|
||||
|
||||
if (typeof name !== "string" || !name.trim()) {
|
||||
return { error: "Participant name is required" };
|
||||
}
|
||||
|
||||
if (typeof sportsSeasonId !== "string" || !sportsSeasonId.trim()) {
|
||||
return { error: "Sports season ID is required" };
|
||||
}
|
||||
|
||||
try {
|
||||
const participant = await createParticipant({
|
||||
name: name.trim(),
|
||||
sportsSeasonId,
|
||||
sportsSeasonId: params.id,
|
||||
});
|
||||
return {
|
||||
success: `Participant "${participant.name}" created.`,
|
||||
|
|
@ -410,6 +413,9 @@ export async function action({ request, params }: Route.ActionArgs) {
|
|||
};
|
||||
} catch (error) {
|
||||
logger.error("Error creating participant:", error);
|
||||
if (error instanceof Error && error.message.includes("participants_sports_season_name_unique")) {
|
||||
return { error: `A participant named "${name.trim()}" already exists in this sports season.` };
|
||||
}
|
||||
return { error: "Failed to create participant" };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -417,7 +417,6 @@ export default function EventResults({
|
|||
{event.isQualifyingEvent && event.eventType !== "final_standings" && event.eventType !== "playoff_game" && !event.isComplete && (
|
||||
<BatchResultEntry
|
||||
participants={participants}
|
||||
eventId={event.id}
|
||||
sportsSeasonId={sportsSeason.id}
|
||||
existingResultParticipantIds={
|
||||
new Set(results.map((r: { participant: { id: string } }) => r.participant.id))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue