diff --git a/app/[locale]/(index)/page.tsx b/app/[locale]/(index)/page.tsx index 842523abc..e9a78f89b 100644 --- a/app/[locale]/(index)/page.tsx +++ b/app/[locale]/(index)/page.tsx @@ -2,6 +2,7 @@ import { createUrlSearchParams } from "@acdh-oeaw/lib"; import type { Metadata, ResolvingMetadata } from "next"; import { useTranslations } from "next-intl"; import { getTranslations, unstable_setRequestLocale as setRequestLocale } from "next-intl/server"; +import * as v from "valibot"; import { MainContent } from "@/components/main-content"; import { ResourcesSection } from "@/components/resources-section"; @@ -48,7 +49,7 @@ export default function IndexPage(props: IndexPageProps) { const searchParamsSchema = createResourceFiltersSearchParamsSchema(locale); - const filters = searchParamsSchema.parse({ + const filters = v.parse(searchParamsSchema, { limit: urlSearchParams.get("limit"), locale: urlSearchParams.get("locale"), page: urlSearchParams.get("page"), diff --git a/app/[locale]/curricula/page.tsx b/app/[locale]/curricula/page.tsx index cd371e7dd..868bcdd3c 100644 --- a/app/[locale]/curricula/page.tsx +++ b/app/[locale]/curricula/page.tsx @@ -2,6 +2,7 @@ import { createUrlSearchParams } from "@acdh-oeaw/lib"; import type { Metadata, ResolvingMetadata } from "next"; import { useTranslations } from "next-intl"; import { getTranslations, unstable_setRequestLocale as setRequestLocale } from "next-intl/server"; +import * as v from "valibot"; import { CurriculaSection } from "@/components/curricula-section"; import { MainContent } from "@/components/main-content"; @@ -44,7 +45,7 @@ export default function CurriculaPage(props: CurriculaPageProps) { const searchParamsSchema = createResourceFiltersSearchParamsSchema(locale); - const filters = searchParamsSchema.parse({ + const filters = v.parse(searchParamsSchema, { limit: urlSearchParams.get("limit"), locale: urlSearchParams.get("locale"), page: urlSearchParams.get("page"), diff --git a/app/[locale]/resources/page.tsx b/app/[locale]/resources/page.tsx index aee3dc80f..5c1ea8474 100644 --- a/app/[locale]/resources/page.tsx +++ b/app/[locale]/resources/page.tsx @@ -2,6 +2,7 @@ import { createUrlSearchParams } from "@acdh-oeaw/lib"; import type { Metadata, ResolvingMetadata } from "next"; import { useTranslations } from "next-intl"; import { getTranslations, unstable_setRequestLocale as setRequestLocale } from "next-intl/server"; +import * as v from "valibot"; import { MainContent } from "@/components/main-content"; import { ResourcesSection } from "@/components/resources-section"; @@ -44,7 +45,7 @@ export default function ResourcesPage(props: ResourcesPageProps) { const searchParamsSchema = createResourceFiltersSearchParamsSchema(locale); - const filters = searchParamsSchema.parse({ + const filters = v.parse(searchParamsSchema, { limit: urlSearchParams.get("limit"), locale: urlSearchParams.get("locale"), page: urlSearchParams.get("page"), diff --git a/app/api/metadata/curricula/route.ts b/app/api/metadata/curricula/route.ts index 76d4f1b37..7c71d25fb 100644 --- a/app/api/metadata/curricula/route.ts +++ b/app/api/metadata/curricula/route.ts @@ -3,21 +3,21 @@ import { join } from "node:path"; import { log } from "@acdh-oeaw/lib"; import type { NextRequest } from "next/server"; -import { z } from "zod"; +import * as v from "valibot"; import type { Resource } from "@/lib/content/types"; const filePath = join(process.cwd(), "./public/metadata/curricula.json"); -const searchParamsSchema = z.object({ - limit: z.coerce.number().int().positive().max(100).optional().default(10), - offset: z.coerce.number().int().nonnegative().optional().default(0), +const searchParamsSchema = v.object({ + limit: v.optional(v.coerce(v.number([v.integer(), v.minValue(1), v.maxValue(100)]), Number), 10), + offset: v.optional(v.coerce(v.number([v.integer(), v.minValue(0)]), Number), 0), }); export async function GET(request: NextRequest) { const { searchParams } = request.nextUrl; - const result = searchParamsSchema.safeParse({ + const result = v.safeParse(searchParamsSchema, { limit: searchParams.get("limit"), offset: searchParams.get("offset"), }); @@ -26,7 +26,7 @@ export async function GET(request: NextRequest) { return Response.json({ message: "Bad request" }, { status: 400 }); } - const { offset, limit } = result.data; + const { offset, limit } = result.output; try { const curricula = await readCurricula(); diff --git a/app/api/metadata/resources/route.ts b/app/api/metadata/resources/route.ts index 91b7c72fc..4876a66a9 100644 --- a/app/api/metadata/resources/route.ts +++ b/app/api/metadata/resources/route.ts @@ -3,21 +3,21 @@ import { join } from "node:path"; import { log } from "@acdh-oeaw/lib"; import type { NextRequest } from "next/server"; -import { z } from "zod"; +import * as v from "valibot"; import type { Resource } from "@/lib/content/types"; const filePath = join(process.cwd(), "./public/metadata/resources.json"); -const searchParamsSchema = z.object({ - limit: z.coerce.number().int().positive().max(100).optional().default(10), - offset: z.coerce.number().int().nonnegative().optional().default(0), +const searchParamsSchema = v.object({ + limit: v.optional(v.coerce(v.number([v.integer(), v.minValue(1), v.maxValue(100)]), Number), 10), + offset: v.optional(v.coerce(v.number([v.integer(), v.minValue(0)]), Number), 0), }); export async function GET(request: NextRequest) { const { searchParams } = request.nextUrl; - const result = searchParamsSchema.safeParse({ + const result = v.safeParse(searchParamsSchema, { limit: searchParams.get("limit"), offset: searchParams.get("offset"), }); @@ -26,7 +26,7 @@ export async function GET(request: NextRequest) { return Response.json({ message: "Bad request" }, { status: 400 }); } - const { offset, limit } = result.data; + const { offset, limit } = result.output; try { const resources = await readResources(); diff --git a/components/curricula-filter-form.tsx b/components/curricula-filter-form.tsx index 704dc255b..32378a6f8 100644 --- a/components/curricula-filter-form.tsx +++ b/components/curricula-filter-form.tsx @@ -4,7 +4,7 @@ import { createUrlSearchParams } from "@acdh-oeaw/lib"; import { useOptimistic, useState } from "react"; import { ListBox, ListBoxItem, type Selection } from "react-aria-components"; import { useFormState } from "react-dom"; -import { z } from "zod"; +import * as v from "valibot"; import { SelectField, SelectItem } from "@/components/ui/blocks/select-field"; import { TextInputField } from "@/components/ui/blocks/text-input-field"; @@ -16,13 +16,13 @@ import { getFormData } from "@/lib/get-form-data"; import { useRouter } from "@/lib/navigation"; import { cn } from "@/lib/styles"; -const formSchema = z.object({ - locale: z.enum([...locales, "all"]).optional(), - q: z.string().optional(), - tag: z.array(z.string()).optional(), +const formSchema = v.object({ + locale: v.optional(v.picklist([...locales, "all"])), + q: v.optional(v.string()), + tag: v.optional(v.array(v.string())), }); -type FormSchema = z.infer; +type FormSchema = v.Output; interface CurriculaFilterFormProps { filters: FormSchema; @@ -49,7 +49,7 @@ export function CurriculaFilterForm(props: CurriculaFilterFormProps) { function action(prevState: undefined, formData: FormData) { const input = getFormData(formData); - const filters = formSchema.parse(input); + const filters = v.parse(formSchema, input); updateOptimisticFilters(filters); router.push("?" + String(createUrlSearchParams(filters))); diff --git a/components/resources-filter-form.tsx b/components/resources-filter-form.tsx index c352c9f41..0629e512c 100644 --- a/components/resources-filter-form.tsx +++ b/components/resources-filter-form.tsx @@ -4,7 +4,7 @@ import { createUrlSearchParams } from "@acdh-oeaw/lib"; import { useOptimistic, useState } from "react"; import { ListBox, ListBoxItem, type Selection } from "react-aria-components"; import { useFormState } from "react-dom"; -import { z } from "zod"; +import * as v from "valibot"; import { SelectField, SelectItem } from "@/components/ui/blocks/select-field"; import { TextInputField } from "@/components/ui/blocks/text-input-field"; @@ -16,13 +16,13 @@ import { getFormData } from "@/lib/get-form-data"; import { useRouter } from "@/lib/navigation"; import { cn } from "@/lib/styles"; -const formSchema = z.object({ - locale: z.enum([...locales, "all"]).optional(), - q: z.string().optional(), - tag: z.array(z.string()).optional(), +const formSchema = v.object({ + locale: v.optional(v.picklist([...locales, "all"])), + q: v.optional(v.string()), + tag: v.optional(v.array(v.string())), }); -type FormSchema = z.infer; +type FormSchema = v.Output; interface ResourcesFilterFormProps { filters: FormSchema; @@ -49,7 +49,7 @@ export function ResourcesFilterForm(props: ResourcesFilterFormProps) { function action(prevState: undefined, formData: FormData) { const input = getFormData(formData); - const filters = formSchema.parse(input); + const filters = v.parse(formSchema, input); updateOptimisticFilters(filters); router.push("?" + String(createUrlSearchParams(filters))); diff --git a/lib/schemas/resource-filter-schema.ts b/lib/schemas/resource-filter-schema.ts index a09276282..2d5fea7a6 100644 --- a/lib/schemas/resource-filter-schema.ts +++ b/lib/schemas/resource-filter-schema.ts @@ -1,17 +1,16 @@ -import { z } from "zod"; +import * as v from "valibot"; import { type Locale, locales } from "@/config/i18n.config"; export function createResourceFiltersSearchParamsSchema(locale: Locale) { - return z.object({ - limit: z.coerce.number().int().positive().max(100).optional().default(20).catch(20), - locale: z - .enum([...locales, "all"]) - .optional() - .default(locale) - .catch(locale), - page: z.coerce.number().int().positive().optional().default(1).catch(1), - q: z.string().optional().default("").catch(""), - tag: z.array(z.string()).optional().default([]).catch([]), + return v.object({ + limit: v.fallback( + v.optional(v.coerce(v.number([v.integer(), v.minValue(1), v.maxValue(100)]), Number), 20), + 20, + ), + locale: v.fallback(v.optional(v.picklist([...locales, "all"]), locale), locale), + page: v.fallback(v.optional(v.coerce(v.number([v.integer(), v.minValue(1)]), Number), 1), 1), + q: v.fallback(v.optional(v.string(), ""), ""), + tag: v.fallback(v.optional(v.array(v.string()), []), []), }); }