diff --git a/frontend/src/ts/components/pages/leaderboard/Navigation.tsx b/frontend/src/ts/components/pages/leaderboard/Navigation.tsx index 67b6bca974df..85b0e057fe0f 100644 --- a/frontend/src/ts/components/pages/leaderboard/Navigation.tsx +++ b/frontend/src/ts/components/pages/leaderboard/Navigation.tsx @@ -1,3 +1,4 @@ +import { PageNumberSchema } from "@monkeytype/schemas/util"; import { JSXElement, Setter, Show } from "solid-js"; import { setPage } from "../../../states/leaderboard-selection"; @@ -62,6 +63,18 @@ export function Navigation(props: { { type: "number", placeholder: "Page number", + validation: { + isValid: async (page) => { + const validationResult = PageNumberSchema.safeParse( + Number.parseInt(page), + ); + + if (validationResult.success) return true; + return validationResult.error.errors + .map((err) => err.message) + .join(", "); + }, + }, }, ], buttonText: "Go", diff --git a/frontend/src/ts/states/simple-modal.ts b/frontend/src/ts/states/simple-modal.ts index 911bffbafa38..de950b3bdaac 100644 --- a/frontend/src/ts/states/simple-modal.ts +++ b/frontend/src/ts/states/simple-modal.ts @@ -1,5 +1,4 @@ import { createSignal } from "solid-js"; -import { z } from "zod"; import { showModal, hideModal } from "./modals"; import { @@ -8,7 +7,7 @@ import { showErrorNotification, } from "./notifications"; import { showLoaderBar, hideLoaderBar } from "./loader-bar"; -import { IsValidResponse } from "../types/validation"; +import { Validation } from "../types/validation"; type CommonInput = { type: TType; @@ -20,11 +19,7 @@ type CommonInput = { optional?: boolean; label?: string; oninput?: (event: Event) => void; - validation?: { - schema?: z.Schema; - isValid?: (value: string) => Promise; - debounceDelay?: number; - }; + validation?: Validation; }; export type TextInput = CommonInput<"text", string>; diff --git a/packages/contracts/src/leaderboards.ts b/packages/contracts/src/leaderboards.ts index 767b96375140..73256fbc8565 100644 --- a/packages/contracts/src/leaderboards.ts +++ b/packages/contracts/src/leaderboards.ts @@ -13,6 +13,7 @@ import { import { Mode2Schema, ModeSchema } from "@monkeytype/schemas/shared"; import { initContract } from "@ts-rest/core"; import { LanguageSchema } from "@monkeytype/schemas/languages"; +import { PageNumberSchema } from "@monkeytype/schemas/util"; const LanguageAndModeQuerySchema = z.object({ language: LanguageSchema, @@ -21,7 +22,7 @@ const LanguageAndModeQuerySchema = z.object({ }); const PaginationQuerySchema = z.object({ - page: z.number().int().safe().nonnegative().default(0), + page: PageNumberSchema, pageSize: z.number().int().safe().positive().min(10).max(200).default(50), }); diff --git a/packages/schemas/src/util.ts b/packages/schemas/src/util.ts index 4abbf2c39c47..e5f71357d1d2 100644 --- a/packages/schemas/src/util.ts +++ b/packages/schemas/src/util.ts @@ -64,3 +64,10 @@ export function customEnumErrorHandler(message: string): ZodErrorMap { : (issue.message ?? "Required"), }); } + +export const PageNumberSchema = z + .number() + .int() + .safe() + .nonnegative() + .default(0);