Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@ This project uses a user-facing changelog format.
- Ylläpitäjä voi avata ajokoetapahtuman omalle sivulleen tarkastelua ja nykyisiä muokkaustoimintoja varten.
- Ylläpitäjä voi luoda ajokoetapahtuman perustietoineen ja avata sen omalle sivulleen ennen koetulosten lisäämistä.
- Ylläpitäjä voi poistaa tyhjän ajokoetapahtuman sen omalta sivulta.
- Ylläpitäjä voi lisätä ajokoetapahtumaan koirakohtaisia tuloksia yksi kerrallaan ja jatkaa seuraavaan tulokseen tai palata tapahtuman sivulle.

### Changed

- Viimeisen koetuloksen poistaminen säilyttää ajokoetapahtuman tyhjänä, kunnes ylläpitäjä poistaa tapahtuman erikseen.
- Uuden ajokoetapahtuman luomisen jälkeen siirrytään suoraan lisäämään tapahtuman ensimmäistä koetulosta.

### Fixed

- Ajokoetuloksen lisätietorivit tallennetaan kelvollisessa kokonaislukujärjestyksessä, ja hylätty lisätietorivi näytetään aiempaa tarkemmin.
- Ajokoetuloksen lisäyslomakkeen selaimen Takaisin-toiminto ei enää yritä perua navigointia epäluotettavalla edestakaisella sivuhistorian käsittelyllä.
- Ylisuuria kokonaislukuarvoja ei enää tallenneta tyhjinä ajokoetuloksen luonnissa tai muokkauksessa.

### Removed

## [0.19.0] - 2026-07-20
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it, vi } from "vitest";
import Page from "../page";

vi.mock(
"@/components/admin/trials/admin-trial-entry-create-page-client",
() => ({
AdminTrialEntryCreatePageClient: ({
trialEventId,
}: {
trialEventId: string;
}) => React.createElement("div", null, trialEventId),
}),
);

describe("manual trial result create page", () => {
it("passes the route event id to the client workflow", async () => {
const html = renderToStaticMarkup(
await Page({ params: Promise.resolve({ trialEventId: "event-1" }) }),
);
expect(html).toContain("event-1");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { AdminTrialEntryCreatePageClient } from "@/components/admin/trials/admin-trial-entry-create-page-client";

export default async function AdminTrialEntryCreatePage({
params,
}: {
params: Promise<{ trialEventId: string }>;
}) {
const { trialEventId } = await params;
return <AdminTrialEntryCreatePageClient trialEventId={trialEventId} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,30 @@ describe("createAdminTrialEntryAction", () => {
message: "Conflict",
});
});

it("passes through safe validation details", async () => {
guard.mockResolvedValue({ ok: true });
user.mockResolvedValue({ id: "u1", email: "a@example.com", role: "ADMIN" });
create.mockResolvedValue({
status: 400,
body: {
ok: false,
code: "INVALID_TRIAL_ADDITIONAL_INFO",
error: "Invalid",
details: {
area: "additional_info",
reason: "invalid_lisatieto_order",
koodi: "25",
osa: "b",
},
},
});
await expect(createAdminTrialEntryAction(input)).resolves.toMatchObject({
validationIssue: {
reason: "invalid_lisatieto_order",
koodi: "25",
osa: "b",
},
});
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use server";

import type {
AdminTrialEntryValidationIssue,
CreateAdminTrialEntryRequest,
CreateAdminTrialEntryResponse,
} from "@beagle/contracts";
Expand All @@ -13,8 +14,42 @@ export type CreateAdminTrialEntryActionResult = {
hasError: boolean;
errorCode?: string;
message?: string;
validationIssue?: AdminTrialEntryValidationIssue;
};

const VALIDATION_REASONS = new Set<AdminTrialEntryValidationIssue["reason"]>([
"invalid_write_shape",
"invalid_koetyyppi",
"invalid_huomautus",
"invalid_entry_integer",
"invalid_entry_number",
"missing_eras",
"invalid_era_number",
"duplicate_eras",
"non_continuous_eras",
"invalid_era_integer",
"invalid_era_number_field",
"invalid_lisatieto_code",
"duplicate_lisatieto_key",
"invalid_lisatieto_era",
"duplicate_lisatieto_era_value",
"invalid_lisatieto_order",
]);

function toValidationIssue(
value: unknown,
): AdminTrialEntryValidationIssue | undefined {
if (!value || typeof value !== "object") return undefined;
const candidate = value as Partial<AdminTrialEntryValidationIssue>;
if (
!["entry", "eras", "additional_info"].includes(candidate.area ?? "") ||
!candidate.reason ||
!VALIDATION_REASONS.has(candidate.reason)
)
return undefined;
return candidate as AdminTrialEntryValidationIssue;
}

export async function createAdminTrialEntryAction(
input: CreateAdminTrialEntryRequest,
): Promise<CreateAdminTrialEntryActionResult> {
Expand Down Expand Up @@ -42,11 +77,13 @@ export async function createAdminTrialEntryAction(
{ actorUserId: user.id },
);
if (!result.body.ok) {
const validationIssue = toValidationIssue(result.body.details);
return {
data: null,
hasError: true,
errorCode: result.body.code,
message: result.body.error,
...(validationIssue ? { validationIssue } : {}),
};
}
return { data: result.body.data, hasError: false };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ describe("AdminTrialEventCreatePageClient", () => {
sklKoeId: 456,
});
expect(replaceMock).toHaveBeenCalledWith(
"/admin/trials/event%2Fwith%20spaces",
"/admin/trials/event%2Fwith%20spaces/results/new",
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ describe("AdminTrialEventWorkspacePageClient", () => {
});
expect(panelPropsRef.current?.workspaceHref).toBeUndefined();
expect(panelPropsRef.current?.allowEmptyEventDeletion).toBe(true);
expect(panelPropsRef.current?.createEntryHref).toBe(
"/admin/trials/event-1/results/new",
);
});

it("passes loading state to the reused panel", () => {
Expand Down
Loading
Loading