From 93140268178a2a46e79ce3c54f38c2e102831b4c Mon Sep 17 00:00:00 2001 From: Marco Pedone Date: Thu, 7 Aug 2025 14:50:40 +0200 Subject: [PATCH] refactor: update zod imports to version 4 and adjust error handling - Changed all zod imports from "zod" to "zod/v4" across multiple files. - Updated error handling in forms to use new zod error configuration. - Refactored custom error mapping to align with zod v4 changes. - Adjusted password validation logic to utilize new zod features. - Cleaned up unused locale error mapping code. - Enhanced type definitions for async iterables in zod utility functions. --- apps/infoalloggi/package-lock.json | 46 +++-- apps/infoalloggi/package.json | 4 +- .../components/servizio/annuncio_actions.tsx | 6 +- apps/infoalloggi/src/env.mjs | 6 +- apps/infoalloggi/src/forms/FormBanlist.tsx | 8 +- apps/infoalloggi/src/forms/FormBanners.tsx | 8 +- .../src/forms/FormChangePassword.tsx | 8 +- .../src/forms/FormContattoAnnuncio.tsx | 10 +- apps/infoalloggi/src/forms/FormEtichette.tsx | 10 +- apps/infoalloggi/src/forms/FormFlags.tsx | 8 +- apps/infoalloggi/src/forms/FormLogin.tsx | 10 +- .../infoalloggi/src/forms/FormNewPswReset.tsx | 10 +- .../infoalloggi/src/forms/FormNewServizio.tsx | 33 ++- .../src/forms/FormNewServizioAcquisto.tsx | 83 ++++---- apps/infoalloggi/src/forms/FormNewUser.tsx | 10 +- .../src/forms/FormOverridePassword.tsx | 8 +- apps/infoalloggi/src/forms/FormPrezzo.tsx | 20 +- .../src/forms/FormProfilo_Account.tsx | 10 +- .../src/forms/FormProfilo_Anagrafica.tsx | 45 ++-- apps/infoalloggi/src/forms/FormPwSetup.tsx | 12 +- .../src/forms/FormRstPwFromToken.tsx | 18 +- apps/infoalloggi/src/forms/FormStringhe.tsx | 8 +- apps/infoalloggi/src/hooks/chatHooks.ts | 14 +- apps/infoalloggi/src/hooks/locale.ts | 195 ------------------ .../src/hooks/useStrongPassword.ts | 18 +- apps/infoalloggi/src/lib/form_utils.ts | 57 ++--- apps/infoalloggi/src/lib/zodForm.ts | 21 +- .../admin/edit-annuncio/[id].tsx | 10 +- apps/infoalloggi/src/pages/contatti.tsx | 10 +- .../src/server/api/routers/annunci.ts | 2 +- .../src/server/api/routers/auth.ts | 6 +- .../src/server/api/routers/banlist.ts | 2 +- .../src/server/api/routers/chat.ts | 2 +- .../src/server/api/routers/chat_sse.ts | 2 +- .../src/server/api/routers/comunicazioni.ts | 2 +- .../src/server/api/routers/contact.ts | 4 +- .../src/server/api/routers/event_queue.ts | 2 +- .../src/server/api/routers/fatture.ts | 2 +- .../src/server/api/routers/interests.ts | 2 +- .../src/server/api/routers/messages_sse.ts | 2 +- .../src/server/api/routers/pagamenti.ts | 2 +- .../src/server/api/routers/prezziario.ts | 2 +- .../src/server/api/routers/servizio.ts | 2 +- .../src/server/api/routers/settings.ts | 2 +- .../src/server/api/routers/storage.ts | 2 +- .../src/server/api/routers/stripe.ts | 2 +- .../src/server/api/routers/users.ts | 4 +- apps/infoalloggi/src/server/api/trpc.ts | 6 +- .../server/controllers/annunci.controller.ts | 2 +- .../server/controllers/servizio.controller.ts | 2 +- .../src/server/services/fic.service.ts | 2 +- apps/infoalloggi/src/server/utils/sse_zod.ts | 87 +++----- .../infoalloggi/src/server/utils/zod_types.ts | 9 +- 53 files changed, 333 insertions(+), 525 deletions(-) delete mode 100644 apps/infoalloggi/src/hooks/locale.ts diff --git a/apps/infoalloggi/package-lock.json b/apps/infoalloggi/package-lock.json index 306e7fe..b93d48a 100644 --- a/apps/infoalloggi/package-lock.json +++ b/apps/infoalloggi/package-lock.json @@ -78,7 +78,7 @@ "uuid": "^11.1.0", "vaul": "^1.1.2", "yaml": "^2.8.1", - "zod": "^3.25.76" + "zod": "^4.0.15" }, "devDependencies": { "@eslint/compat": "^1.3.1", @@ -127,7 +127,7 @@ "prettier-plugin-tailwindcss": "^0.6.14", "tailwind-scrollbar": "^4.0.2", "tailwindcss": "^4.1.11", - "trpc-ui": "https://gitpkg.vercel.app/jrhe/trpc-ui/packages/trpc-ui?feature/zod-v4-support&scripts.postinstall=npm%20install%20--ignore-scripts%20%26%26%20npm%20run%20build", + "trpc-ui": "https://gitpkg.vercel.app/jrhe/trpc-ui/packages/trpc-ui?feature/zod-v4-support&scripts.postinstall=%26%26%20npm%20install%20pnpm%20%26%26%20npm%20install%20--ignore-scripts%20%26%26%20npm%20run%20build", "tw-animate-css": "^1.3.6", "typescript": "^5.9.2", "typescript-eslint": "^8.39.0" @@ -13830,6 +13830,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/knip/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/kysely": { "version": "0.28.4", "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.4.tgz", @@ -20092,8 +20102,8 @@ }, "node_modules/trpc-ui": { "version": "1.0.15", - "resolved": "https://gitpkg.vercel.app/jrhe/trpc-ui/packages/trpc-ui?feature/zod-v4-support&scripts.postinstall=npm%20install%20--ignore-scripts%20%26%26%20npm%20run%20build", - "integrity": "sha512-E1FvgLyMzBFneyBxmYaW4FV4IaUjOcfSyN1fRpMdc3160/fePfSkUVP015FW4qNAuLc4hhQyOVXqefWquR9FUg==", + "resolved": "https://gitpkg.vercel.app/jrhe/trpc-ui/packages/trpc-ui?feature/zod-v4-support&scripts.postinstall=%26%26%20npm%20install%20pnpm%20%26%26%20npm%20install%20--ignore-scripts%20%26%26%20npm%20run%20build", + "integrity": "sha512-3cw5HsTSl114eZQH1SGm3NWzb0JnDIdyB3Ins2kELHMFBax2Y7v2GLDAh5vkOc1IGqSwYg3gjbMLytKEug2B9w==", "dev": true, "hasInstallScript": true, "license": "ISC", @@ -20188,6 +20198,16 @@ "url": "https://github.com/sponsors/dcastil" } }, + "node_modules/trpc-ui/node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -21279,24 +21299,14 @@ } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.15.tgz", + "integrity": "sha512-2IVHb9h4Mt6+UXkyMs0XbfICUh1eUrlJJAOupBHUhLRnKkruawyDddYRCs0Eizt900ntIMk9/4RksYl+FgSpcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/zod-to-json-schema": { - "version": "3.24.6", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", - "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", - "dev": true, - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } - }, "node_modules/zod-validation-error": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.3.tgz", @@ -21351,4 +21361,4 @@ } } } -} \ No newline at end of file +} diff --git a/apps/infoalloggi/package.json b/apps/infoalloggi/package.json index cef9464..2322950 100644 --- a/apps/infoalloggi/package.json +++ b/apps/infoalloggi/package.json @@ -89,7 +89,7 @@ "uuid": "^11.1.0", "vaul": "^1.1.2", "yaml": "^2.8.1", - "zod": "^3.25.76" + "zod": "^4.0.15" }, "devDependencies": { "@eslint/compat": "^1.3.1", @@ -138,8 +138,8 @@ "prettier-plugin-tailwindcss": "^0.6.14", "tailwind-scrollbar": "^4.0.2", "tailwindcss": "^4.1.11", - "tw-animate-css": "^1.3.6", "trpc-ui": "https://gitpkg.vercel.app/jrhe/trpc-ui/packages/trpc-ui?feature/zod-v4-support&scripts.postinstall=%26%26%20npm%20install%20pnpm%20%26%26%20npm%20install%20--ignore-scripts%20%26%26%20npm%20run%20build", + "tw-animate-css": "^1.3.6", "typescript": "^5.9.2", "typescript-eslint": "^8.39.0" }, diff --git a/apps/infoalloggi/src/components/servizio/annuncio_actions.tsx b/apps/infoalloggi/src/components/servizio/annuncio_actions.tsx index b319379..0caa573 100644 --- a/apps/infoalloggi/src/components/servizio/annuncio_actions.tsx +++ b/apps/infoalloggi/src/components/servizio/annuncio_actions.tsx @@ -50,7 +50,7 @@ import { LoadingPage } from "~/components/loading"; import Link from "next/link"; import { cn } from "~/lib/utils"; import { useRef, useState } from "react"; -import { z } from "zod"; +import { z } from "zod/v4"; import { Separator } from "~/components/ui/separator"; import { Card, @@ -152,8 +152,8 @@ export const AnnuncioActions = () => { const ContrattoImportSchema = z.object({ gestionale_id: z.number().nullable(), - contratto_decorrenza: z.coerce.date().nullable(), - contratto_scadenza: z.coerce.date().nullable(), + contratto_decorrenza: z.date().nullable(), + contratto_scadenza: z.date().nullable(), contratto_tipo: z .enum([ "Transitorio", diff --git a/apps/infoalloggi/src/env.mjs b/apps/infoalloggi/src/env.mjs index 7d5de74..6fef447 100644 --- a/apps/infoalloggi/src/env.mjs +++ b/apps/infoalloggi/src/env.mjs @@ -1,7 +1,7 @@ // @ts-nocheck /* eslint-disable */ -import { z } from "zod"; +import { z } from "zod/v4"; /** * Specify your server-side environment variables schema here. This way you can ensure the app isn't @@ -9,7 +9,7 @@ import { z } from "zod"; */ const server = z.object({ NODE_ENV: z.enum(["development", "test", "production"]), - INTERNAL_BASE_URL: z.string().url(), + INTERNAL_BASE_URL: z.url(), PGHOST: z.string(), POSTGRES_USER: z.string(), POSTGRES_DB: z.string(), @@ -96,7 +96,7 @@ if (!!process.env.SKIP_ENV_VALIDATION === false) { if (parsed.success === false) { console.error( "❌ Invalid environment variables:", - parsed.error.flatten().fieldErrors, + z.treeifyError(parsed.error).fieldErrors, ); throw new Error("Invalid environment variables"); } diff --git a/apps/infoalloggi/src/forms/FormBanlist.tsx b/apps/infoalloggi/src/forms/FormBanlist.tsx index 897d015..d76a1ba 100644 --- a/apps/infoalloggi/src/forms/FormBanlist.tsx +++ b/apps/infoalloggi/src/forms/FormBanlist.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -7,7 +7,6 @@ import { FormLabel, FormMessage, } from "~/components/custom_ui/form"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { useZodForm } from "~/lib/zodForm"; import Input from "~/components/custom_ui/input"; import { Button } from "~/components/ui/button"; @@ -54,10 +53,9 @@ export const FormBanlist = ({ }; } - z.setErrorMap(customErrorMapWrapper("it")); + z.config(z.locales.it()); - const form = useZodForm({ - schema: Schema, + const form = useZodForm(Schema, { defaultValues: defaultValues, }); diff --git a/apps/infoalloggi/src/forms/FormBanners.tsx b/apps/infoalloggi/src/forms/FormBanners.tsx index f8dead9..8639ad5 100644 --- a/apps/infoalloggi/src/forms/FormBanners.tsx +++ b/apps/infoalloggi/src/forms/FormBanners.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -7,7 +7,6 @@ import { FormLabel, FormMessage, } from "~/components/custom_ui/form"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { useZodForm } from "~/lib/zodForm"; import Input from "~/components/custom_ui/input"; import { Button } from "~/components/ui/button"; @@ -73,10 +72,9 @@ export const FormBanners = ({ }; } - z.setErrorMap(customErrorMapWrapper("it")); + z.config(z.locales.it()); - const form = useZodForm({ - schema: Schema, + const form = useZodForm(Schema, { defaultValues: defaultValues, }); diff --git a/apps/infoalloggi/src/forms/FormChangePassword.tsx b/apps/infoalloggi/src/forms/FormChangePassword.tsx index ae70c85..8ef4755 100644 --- a/apps/infoalloggi/src/forms/FormChangePassword.tsx +++ b/apps/infoalloggi/src/forms/FormChangePassword.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { useZodForm } from "~/lib/zodForm"; import { Form, @@ -11,7 +11,6 @@ import { import Input from "~/components/custom_ui/input"; import { Button } from "~/components/ui/button"; import toast from "react-hot-toast"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { api } from "~/utils/api"; import { useTranslation } from "~/providers/I18nProvider"; import { useStrongPassword } from "~/hooks/useStrongPassword"; @@ -45,10 +44,9 @@ export const ChangePasswordForm = () => { const { locale, t } = useTranslation(); - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); - const form = useZodForm({ - schema: ChangePasswordFormSchema, + const form = useZodForm(ChangePasswordFormSchema, { defaultValues: { oldpassword: "", newpassword: "", diff --git a/apps/infoalloggi/src/forms/FormContattoAnnuncio.tsx b/apps/infoalloggi/src/forms/FormContattoAnnuncio.tsx index 4b9c579..f901dbc 100644 --- a/apps/infoalloggi/src/forms/FormContattoAnnuncio.tsx +++ b/apps/infoalloggi/src/forms/FormContattoAnnuncio.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -9,7 +9,6 @@ import { } from "~/components/custom_ui/form"; import Input from "~/components/custom_ui/input"; import { Textarea } from "~/components/custom_ui/textarea"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { useZodForm } from "~/lib/zodForm"; import { api } from "~/utils/api"; import { useTranslation } from "~/providers/I18nProvider"; @@ -32,7 +31,7 @@ export const FormContattoAnnucio = ({ codice }: { codice: string }) => { const schema = z.object({ nome: z.string().nonempty("Inserisci un nome valido"), - email: z.string().email(), + email: z.email(), messaggio: z.string().nonempty("Inserisci un messaggio"), telefono: z.string().nonempty("Inserisci un numero di telefono"), checkbox: z.boolean().refine((val) => val === false), // Honeypot field @@ -47,8 +46,7 @@ export const FormContattoAnnucio = ({ codice }: { codice: string }) => { ); type FromValues = z.infer; - const form = useZodForm({ - schema: schema, + const form = useZodForm(schema, { defaultValues: { ...value, messaggio: "", @@ -89,7 +87,7 @@ export const FormContattoAnnucio = ({ codice }: { codice: string }) => { codice, }); }; - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); return (
diff --git a/apps/infoalloggi/src/forms/FormEtichette.tsx b/apps/infoalloggi/src/forms/FormEtichette.tsx index 847550e..1ac8f68 100644 --- a/apps/infoalloggi/src/forms/FormEtichette.tsx +++ b/apps/infoalloggi/src/forms/FormEtichette.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -7,7 +7,6 @@ import { FormLabel, FormMessage, } from "~/components/custom_ui/form"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { useZodForm } from "~/lib/zodForm"; import Input from "~/components/custom_ui/input"; @@ -52,16 +51,15 @@ export const FormEtichette = ({ }; } - z.setErrorMap(customErrorMapWrapper("it")); + z.config(z.locales.it()); - const form = useZodForm({ - schema: Schema, + const form = useZodForm(Schema, { defaultValues: defaultValues, }); function onSubmit(fields: FormValues) { const values = { - id_etichetta: fields.id_etichetta as EtichetteIdEtichetta, + id_etichetta: fields.id_etichetta, title: fields.title, color_hex: fields.color_hex, }; diff --git a/apps/infoalloggi/src/forms/FormFlags.tsx b/apps/infoalloggi/src/forms/FormFlags.tsx index 171701b..24ad1e6 100644 --- a/apps/infoalloggi/src/forms/FormFlags.tsx +++ b/apps/infoalloggi/src/forms/FormFlags.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -7,7 +7,6 @@ import { FormLabel, FormMessage, } from "~/components/custom_ui/form"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { useZodForm } from "~/lib/zodForm"; import Input from "~/components/custom_ui/input"; import { Button } from "~/components/ui/button"; @@ -46,10 +45,9 @@ export const FormFlags = ({ }; } - z.setErrorMap(customErrorMapWrapper("it")); + z.config(z.locales.it()); - const form = useZodForm({ - schema: Schema, + const form = useZodForm(Schema, { defaultValues: defaultValues, }); diff --git a/apps/infoalloggi/src/forms/FormLogin.tsx b/apps/infoalloggi/src/forms/FormLogin.tsx index 6f018e5..c8f478f 100644 --- a/apps/infoalloggi/src/forms/FormLogin.tsx +++ b/apps/infoalloggi/src/forms/FormLogin.tsx @@ -1,5 +1,4 @@ -import { customErrorMapWrapper } from "~/hooks/locale"; -import { z } from "zod"; +import { z } from "zod/v4"; import { useZodForm } from "~/lib/zodForm"; import { Form, @@ -25,7 +24,7 @@ export const FormLogin = () => { const router = useRouter(); const { redirect } = router.query; const LoginSchema = z.object({ - email: z.string().email("Inserisci una email valida"), + email: z.email(), password: z.string(), checkbox: z.boolean().refine((val) => val === false), // Honeypot field }); @@ -38,10 +37,9 @@ export const FormLogin = () => { checkbox: false, // Honeypot field }; const { locale, t } = useTranslation(); - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); - const form = useZodForm({ - schema: LoginSchema, + const form = useZodForm(LoginSchema, { defaultValues: defaultValues, }); diff --git a/apps/infoalloggi/src/forms/FormNewPswReset.tsx b/apps/infoalloggi/src/forms/FormNewPswReset.tsx index 7fd86cf..819ccd8 100644 --- a/apps/infoalloggi/src/forms/FormNewPswReset.tsx +++ b/apps/infoalloggi/src/forms/FormNewPswReset.tsx @@ -1,5 +1,4 @@ -import { customErrorMapWrapper } from "~/hooks/locale"; -import { z } from "zod"; +import { z } from "zod/v4"; import { useZodForm } from "~/lib/zodForm"; import { Form, @@ -20,7 +19,7 @@ import { useState } from "react"; export const FormPswReset = () => { const router = useRouter(); const LoginSchema = z.object({ - email: z.string().email("Inserisci una email valida"), + email: z.email(), }); type LoginFormValues = z.infer; @@ -29,10 +28,9 @@ export const FormPswReset = () => { email: "", }; const { locale, t } = useTranslation(); - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); - const form = useZodForm({ - schema: LoginSchema, + const form = useZodForm(LoginSchema, { defaultValues: defaultValues, }); diff --git a/apps/infoalloggi/src/forms/FormNewServizio.tsx b/apps/infoalloggi/src/forms/FormNewServizio.tsx index 85e233a..0bf785d 100644 --- a/apps/infoalloggi/src/forms/FormNewServizio.tsx +++ b/apps/infoalloggi/src/forms/FormNewServizio.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -8,7 +8,6 @@ import { FormLabel, FormMessage, } from "~/components/custom_ui/form"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { useZodForm } from "~/lib/zodForm"; import { Button } from "~/components/ui/button"; import { useTranslation } from "~/providers/I18nProvider"; @@ -22,13 +21,13 @@ import { Label } from "~/components/ui/label"; import { Badge } from "~/components/ui/badge"; import type { Servizio } from "~/schemas/public/Servizio"; -const schema = z +const Schema = z .object({ tipologia: z.custom(), - budget: z.coerce.number(), + budget: z.number(), entromese: z.number().nullable(), - n_adulti: z.coerce.number().min(1), - n_minori: z.coerce.number(), + n_adulti: z.number().min(1), + n_minori: z.number(), permanenza: z.number().nullable(), motivazione_transitorio: z.string().nullable(), reddito: z.string().nullable(), @@ -43,15 +42,16 @@ const schema = z }) .superRefine(({ n_adulti }, refinementContext) => { if (n_adulti <= 0) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci il numero di persone", path: ["npersone"], + input: "", }); } }); -export type FormValues = z.infer; +export type FormValues = z.infer; export const FormNewServizio = ({ initialData, @@ -91,10 +91,9 @@ export const FormNewServizio = ({ } const { locale, t } = useTranslation(); - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); - const form = useZodForm({ - schema: schema, + const form = useZodForm(Schema, { defaultValues: defaultValues, }); @@ -181,7 +180,7 @@ export const FormNewServizio = ({ step={50} defaultValue={[field.value]} onValueChange={(vals) => { - field.onChange(vals[0]); + field.onChange(Number(vals[0])); }} rangeClassName="bg-neutral-700" thumbClassName="border-neutral-700" @@ -313,8 +312,8 @@ export const FormNewServizio = ({
field.onChange(Number(value))} name="n_adulti" onlyPositive /> @@ -338,8 +337,8 @@ export const FormNewServizio = ({ field.onChange(Number(value))} name="n_minori" onlyPositive /> diff --git a/apps/infoalloggi/src/forms/FormNewServizioAcquisto.tsx b/apps/infoalloggi/src/forms/FormNewServizioAcquisto.tsx index 4139753..73f0000 100644 --- a/apps/infoalloggi/src/forms/FormNewServizioAcquisto.tsx +++ b/apps/infoalloggi/src/forms/FormNewServizioAcquisto.tsx @@ -1,5 +1,5 @@ "use client"; -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -10,7 +10,6 @@ import { FormLabel, FormMessage, } from "~/components/custom_ui/form"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { useZodForm } from "~/lib/zodForm"; import { api } from "~/utils/api"; import { Button } from "~/components/ui/button"; @@ -79,7 +78,7 @@ export const FormNewServizioAcquisto = ({ isAdmin?: boolean; }) => { const router = useRouter(); - const schema = z + const Schema = z .object({ nome: z.string().nonempty("Inserisci un nome valido"), cognome: z.string().nonempty("Inserisci un cognome valido"), @@ -90,10 +89,10 @@ export const FormNewServizioAcquisto = ({ sede_legale: z.string().nullable(), legale_rappresentante: z.string().nullable(), p_iva: z.string().nullable(), - email: z.string().email(), + email: z.email(), telefono: z.string(), sesso: z.string(), - data_nascita: z.coerce.date(), + data_nascita: z.date(), luogo_nascita: z.string(), nazione_nascita: z.string(), codice_fiscale: z.string(), @@ -108,10 +107,10 @@ export const FormNewServizioAcquisto = ({ .string() .refine((v) => v !== "", { params: { type: "nazione" } }), tipologia: z.custom(), - budget: z.coerce.number().positive(), + budget: z.number().positive(), entromese: z.number().nullable(), - n_adulti: z.coerce.number().min(1), - n_minori: z.coerce.number(), + n_adulti: z.number().min(1), + n_minori: z.number(), permanenza: z.number().nullable(), motivazione_transitorio: z.string().nullable(), reddito: z.string().nullable(), @@ -152,57 +151,64 @@ export const FormNewServizioAcquisto = ({ refinementContext, ) => { if (entromese === null) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci il mese di inizio", path: ["entromese"], + input: "", }); } if (azienda) { if (!ragione_sociale || ragione_sociale.length < 1) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci la ragione sociale", path: ["ragione_sociale"], + input: "", }); } if (!sede_legale || sede_legale.length < 1) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci la sede legale", path: ["sede_legale"], + input: "", }); } if (!p_iva || p_iva.length < 1) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci la partita iva", path: ["p_iva"], + input: "", }); } if (!legale_rappresentante || legale_rappresentante.length < 1) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci il legale rappresentante", path: ["legale_rappresentante"], + input: "", }); } } if (fatturazione_aziendale) { if (!codice_destinatario || codice_destinatario.length != 7) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci un codice destinatario valido", path: ["codice_destinatario"], + input: "", }); } } if (!telefono || telefono.length < 10) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Il numero di telefono deve essere lungo almeno 10 caratteri", + input: "", }); } checkFiscalCodeValidity({ @@ -217,20 +223,22 @@ export const FormNewServizioAcquisto = ({ nazioneNascita: nazione_nascita, }); if (n_adulti <= 0) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci il numero di persone", path: ["n_adulti"], + input: "", }); } if ( permanenza === null && tipologia === TipologiaPosizioneEnum.Transitorio ) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci permanenza", path: ["permanenza"], + input: "", }); } if (tipologia === TipologiaPosizioneEnum.Transitorio) { @@ -238,25 +246,27 @@ export const FormNewServizioAcquisto = ({ motivazione_transitorio === "" || motivazione_transitorio === null ) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci la motivazione", path: ["motivazione_transitorio"], + input: "", }); } } else { if (reddito === "" || reddito === null) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci il reddito", path: ["reddito"], + input: "", }); } } }, ); - type FormValues = z.infer; + type FormValues = z.infer; // This can come from your database or API. const defaultValues: FormValues = { @@ -290,10 +300,9 @@ export const FormNewServizioAcquisto = ({ const { locale, t } = useTranslation(); const nazioni_options = GetNazioni(); const comuni_options = getComuniNames(); - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); - const form = useZodForm({ - schema: schema, + const form = useZodForm(Schema, { defaultValues: defaultValues, }); const DatePickerLocale = locale === "it" ? it : enUS; @@ -478,7 +487,7 @@ export const FormNewServizioAcquisto = ({ step={50} defaultValue={[field.value]} onValueChange={(vals) => { - field.onChange(vals[0]); + field.onChange(Number(vals[0])); }} rangeClassName="bg-neutral-700" thumbClassName="border-neutral-700" @@ -593,7 +602,7 @@ export const FormNewServizioAcquisto = ({ field.onChange(Number(v))} name="n_adulti" onlyPositive /> @@ -615,7 +624,7 @@ export const FormNewServizioAcquisto = ({ field.onChange(Number(v))} name="n_minori" onlyPositive /> diff --git a/apps/infoalloggi/src/forms/FormNewUser.tsx b/apps/infoalloggi/src/forms/FormNewUser.tsx index f209c86..cac02cc 100644 --- a/apps/infoalloggi/src/forms/FormNewUser.tsx +++ b/apps/infoalloggi/src/forms/FormNewUser.tsx @@ -1,5 +1,5 @@ import toast from "react-hot-toast"; -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -8,7 +8,6 @@ import { FormLabel, FormMessage, } from "~/components/custom_ui/form"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { useZodForm } from "~/lib/zodForm"; import { api } from "~/utils/api"; import Input from "~/components/custom_ui/input"; @@ -36,7 +35,7 @@ export const FormNewUser = ({ const NewUserSchema = z .object({ - email: z.string().email("Inserisci una email valida"), + email: z.email(), password: z.string(), nome: z.string().nonempty("Inserisci un nome valido"), cognome: z.string().nonempty("Inserisci un cognome valido"), @@ -61,10 +60,9 @@ export const FormNewUser = ({ }; const { locale, t } = useTranslation(); - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); - const form = useZodForm({ - schema: NewUserSchema, + const form = useZodForm(NewUserSchema, { defaultValues: defaultValues, }); diff --git a/apps/infoalloggi/src/forms/FormOverridePassword.tsx b/apps/infoalloggi/src/forms/FormOverridePassword.tsx index 422a21e..5e2b61d 100644 --- a/apps/infoalloggi/src/forms/FormOverridePassword.tsx +++ b/apps/infoalloggi/src/forms/FormOverridePassword.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { useZodForm } from "~/lib/zodForm"; import { Form, @@ -11,7 +11,6 @@ import { import Input from "~/components/custom_ui/input"; import { Button, buttonVariants } from "~/components/ui/button"; import toast from "react-hot-toast"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { api } from "~/utils/api"; import { useTranslation } from "~/providers/I18nProvider"; import type { UsersId } from "~/schemas/public/Users"; @@ -57,10 +56,9 @@ export const OverridePasswordForm = ({ id }: { id: UsersId }) => { const { locale, t } = useTranslation(); - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); - const form = useZodForm({ - schema: OverridePasswordFormSchema, + const form = useZodForm(OverridePasswordFormSchema, { defaultValues: { password: "", }, diff --git a/apps/infoalloggi/src/forms/FormPrezzo.tsx b/apps/infoalloggi/src/forms/FormPrezzo.tsx index f9803d3..693d1c4 100644 --- a/apps/infoalloggi/src/forms/FormPrezzo.tsx +++ b/apps/infoalloggi/src/forms/FormPrezzo.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -7,7 +7,6 @@ import { FormLabel, FormMessage, } from "~/components/custom_ui/form"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { useZodForm } from "~/lib/zodForm"; import Input from "~/components/custom_ui/input"; import { Button } from "~/components/ui/button"; @@ -48,7 +47,7 @@ export const FormPrezzo = ({ const PrezzoSchema = z .object({ idprezziario: z.custom(), - prezzo_cent: z.coerce.number().positive(), + prezzo_cent: z.number().positive(), testo_condizioni: z.string().nullable(), isAcconto: z.boolean(), isSaldo: z.boolean(), @@ -62,7 +61,7 @@ export const FormPrezzo = ({ isTransitorio: z.boolean(), isStabile: z.boolean(), }) - .superRefine(({ testo_condizioni }, refinementContext) => { + .superRefine(({ testo_condizioni }, ctx) => { if (testo_condizioni) { if ( stringhe_condizioni_options.length > 0 && @@ -73,9 +72,11 @@ export const FormPrezzo = ({ "stringhe_condizioni_options", stringhe_condizioni_options, ); - return refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + + ctx.addIssue({ + code: "custom", message: "Testo condizioni non valido", + input: testo_condizioni, path: ["testo_condizioni"], }); } @@ -102,10 +103,9 @@ export const FormPrezzo = ({ isStabile: false, }; - z.setErrorMap(customErrorMapWrapper("it")); + z.config(z.locales.it()); - const form = useZodForm({ - schema: PrezzoSchema, + const form = useZodForm(PrezzoSchema, { defaultValues: defaultValues, }); @@ -238,7 +238,7 @@ export const FormPrezzo = ({ {...field} type="number" id="prezzo" - value={field.value / 100} + value={Number(field.value) / 100} onChange={(v) => { field.onChange(() => parseInt(v.target.value) * 100); }} diff --git a/apps/infoalloggi/src/forms/FormProfilo_Account.tsx b/apps/infoalloggi/src/forms/FormProfilo_Account.tsx index 287260b..2b83784 100644 --- a/apps/infoalloggi/src/forms/FormProfilo_Account.tsx +++ b/apps/infoalloggi/src/forms/FormProfilo_Account.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { useZodForm } from "~/lib/zodForm"; import { Form, @@ -11,7 +11,6 @@ import { import Input from "~/components/custom_ui/input"; import { Button } from "~/components/ui/button"; import toast from "react-hot-toast"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { api } from "~/utils/api"; import { useTranslation } from "~/providers/I18nProvider"; import type { Users, UsersId } from "~/schemas/public/Users"; @@ -36,7 +35,7 @@ export const ProfileFormAccount = ({ const profileFormSchema = z.object({ nome: z.string().nonempty("Inserisci un nome valido"), cognome: z.string().nonempty("Inserisci un cognome valido"), - email: z.string().email(), + email: z.email(), telefono: z.string().nonempty("Inserisci un numero di telefono"), isAdmin: z.boolean().optional(), }); @@ -51,10 +50,9 @@ export const ProfileFormAccount = ({ isAdmin: isAdmin, }; const { locale, t } = useTranslation(); - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); - const form = useZodForm({ - schema: profileFormSchema, + const form = useZodForm(profileFormSchema, { defaultValues: defaultValues, }); diff --git a/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx b/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx index f3a9283..a4621a7 100644 --- a/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx +++ b/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { useZodForm } from "~/lib/zodForm"; import { Form, @@ -11,7 +11,6 @@ import { import Input from "~/components/custom_ui/input"; import { Button } from "~/components/ui/button"; import toast from "react-hot-toast"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { api } from "~/utils/api"; import { useTranslation } from "~/providers/I18nProvider"; import { differenceInYears, format } from "date-fns"; @@ -44,7 +43,7 @@ export const ProfileFormAnagrafica = ({ }) => { const profileFormSchema = z .object({ - data_nascita: z.coerce.date().optional(), + data_nascita: z.date().optional(), luogo_nascita: z.string().optional(), sesso: z.string().optional(), nazione_nascita: z.string().optional(), @@ -84,49 +83,55 @@ export const ProfileFormAnagrafica = ({ ) => { if (azienda) { if (!ragione_sociale || ragione_sociale.length < 1) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci la ragione sociale", path: ["ragione_sociale"], + input: "", }); } if (!sede_legale || sede_legale.length < 1) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci la sede legale", path: ["sede_legale"], + input: "", }); } if (!p_iva || p_iva.length < 1) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci la partita iva", path: ["p_iva"], + input: "", }); } if (!legale_rappresentante || legale_rappresentante.length < 1) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci il legale rappresentante", path: ["legale_rappresentante"], + input: "", }); } } if (fatturazione_aziendale) { if (!codice_destinatario || codice_destinatario.length != 7) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci il codice destinatario", path: ["codice_destinatario"], + input: "", }); } } if (cap_residenza) { if (cap_residenza.length !== 5) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci un CAP valido", + input: "", }); } } @@ -134,10 +139,11 @@ export const ProfileFormAnagrafica = ({ if (data_nascita) { const diff = differenceInYears(data_nascita, new Date()); if (diff === 0) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci una data valida", path: ["data_nascita"], + input: "", }); } } @@ -188,10 +194,9 @@ export const ProfileFormAnagrafica = ({ codice_destinatario: userData.codice_destinatario || null, }; const { locale, t } = useTranslation(); - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); - const form = useZodForm({ - schema: profileFormSchema, + const form = useZodForm(profileFormSchema, { defaultValues: defaultValues, }); diff --git a/apps/infoalloggi/src/forms/FormPwSetup.tsx b/apps/infoalloggi/src/forms/FormPwSetup.tsx index 37e68a0..fdd5721 100644 --- a/apps/infoalloggi/src/forms/FormPwSetup.tsx +++ b/apps/infoalloggi/src/forms/FormPwSetup.tsx @@ -1,5 +1,4 @@ -import { customErrorMapWrapper } from "~/hooks/locale"; -import { z } from "zod"; +import { z } from "zod/v4"; import { useZodForm } from "~/lib/zodForm"; import { Form, @@ -40,7 +39,7 @@ export const FormPwSetup = ({ user, servizioId }: FormPwSetupProps) => { toggleVisibility, } = useStrongPassword(); - const shema = z + const Schema = z .object({ password: z.string(), checkbox: z.boolean().refine((val) => val === false), // Honeypot field @@ -53,17 +52,16 @@ export const FormPwSetup = ({ user, servizioId }: FormPwSetupProps) => { }); }); - type LoginFormValues = z.infer; + type LoginFormValues = z.infer; const defaultValues: LoginFormValues = { password: "", checkbox: false, // Honeypot field }; const { locale, t } = useTranslation(); - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); - const form = useZodForm({ - schema: shema, + const form = useZodForm(Schema, { defaultValues: defaultValues, }); diff --git a/apps/infoalloggi/src/forms/FormRstPwFromToken.tsx b/apps/infoalloggi/src/forms/FormRstPwFromToken.tsx index 1eafd32..1ac8097 100644 --- a/apps/infoalloggi/src/forms/FormRstPwFromToken.tsx +++ b/apps/infoalloggi/src/forms/FormRstPwFromToken.tsx @@ -8,8 +8,7 @@ import { FormMessage, } from "~/components/custom_ui/form"; import Input from "~/components/custom_ui/input"; -import { z } from "zod"; -import { customErrorMapWrapper } from "~/hooks/locale"; +import { z } from "zod/v4"; import { useZodForm } from "~/lib/zodForm"; import { api } from "~/utils/api"; import { useTranslation } from "~/providers/I18nProvider"; @@ -63,22 +62,24 @@ export const FormRstPwFromToken = (props: { token: string }) => { const complexity = checkStrength(password); if (complexity.filter((req) => req.met).length !== complexity.length) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: complexity .filter((req) => !req.met) .map((req) => req.text) .join("\n"), path: ["password"], + input: "", }); } setStrengthScore(complexity.filter((req) => req.met).length); if (password !== repeat_password) { - refinementContext.addIssue({ + refinementContext.issues.push({ message: t.pwReset.pwDiverse, - code: z.ZodIssueCode.custom, + code: "custom", path: ["repeat_password"], + input: "", }); } }); @@ -93,8 +94,7 @@ export const FormRstPwFromToken = (props: { token: string }) => { }, }); - const form = useZodForm({ - schema: ResetFormSchema, + const form = useZodForm(ResetFormSchema, { defaultValues: { password: "", repeat_password: "", @@ -106,7 +106,7 @@ export const FormRstPwFromToken = (props: { token: string }) => { resetToken: props.token, }); }; - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); return ( <> diff --git a/apps/infoalloggi/src/forms/FormStringhe.tsx b/apps/infoalloggi/src/forms/FormStringhe.tsx index 2b7ef4a..1ffd313 100644 --- a/apps/infoalloggi/src/forms/FormStringhe.tsx +++ b/apps/infoalloggi/src/forms/FormStringhe.tsx @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -7,7 +7,6 @@ import { FormLabel, FormMessage, } from "~/components/custom_ui/form"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { useZodForm } from "~/lib/zodForm"; import Input from "~/components/custom_ui/input"; import { Button } from "~/components/ui/button"; @@ -48,10 +47,9 @@ export const FormStringhe = ({ }; } - z.setErrorMap(customErrorMapWrapper("it")); + z.config(z.locales.it()); - const form = useZodForm({ - schema: Schema, + const form = useZodForm(Schema, { defaultValues: defaultValues, }); diff --git a/apps/infoalloggi/src/hooks/chatHooks.ts b/apps/infoalloggi/src/hooks/chatHooks.ts index fde7967..b13ff05 100644 --- a/apps/infoalloggi/src/hooks/chatHooks.ts +++ b/apps/infoalloggi/src/hooks/chatHooks.ts @@ -7,6 +7,18 @@ import type { ChatMessage } from "~/server/services/messages.service"; import { type OnlineStatus, type WhoIsTyping } from "~/server/sse"; import { api } from "~/utils/api"; +type EditEvent = { + eventType: "edit"; + data: { messageId: MessagesMessageid; message: string }; +}; + +type DeleteEvent = { + eventType: "delete"; + data: MessagesMessageid; +}; + +type MessageUpdate = EditEvent | DeleteEvent; + /** * Set isTyping with a throttle of 1s * Triggers immediately if state changes @@ -167,7 +179,7 @@ export const useLiveChat = (chatId: ChatsChatid, userData: Session) => { api.messagesSSE.onMessageEvent.useSubscription( { chatId }, { - onData(data) { + onData(data: { data: MessageUpdate }) { switch (data.data.eventType) { case "edit": handleEdit(data.data.data); diff --git a/apps/infoalloggi/src/hooks/locale.ts b/apps/infoalloggi/src/hooks/locale.ts deleted file mode 100644 index 9246f35..0000000 --- a/apps/infoalloggi/src/hooks/locale.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { eng, ita } from "~/i18n/FormErrorsTranslation"; - -import * as z from "zod"; - -const Capitalize = (s: string) => { - if (typeof s !== "string") return ""; - return s.charAt(0).toUpperCase() + s.slice(1); -}; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type ErrorType = Record; -function FormErrorTranslation(template: string, obj: ErrorType): string { - return template.replace(/{{[^}]*}}/g, (match) => - typeof obj[match.replace(/{|}/g, "")] === "string" - ? Capitalize(String(obj[match.replace(/{|}/g, "")])) - : String(obj[match.replace(/{|}/g, "")]), - ); -} - -export const customErrorMapWrapper = (locale: string) => { - const data = locale === "it" ? ita : eng; - - const customErrorMap: z.ZodErrorMap = (issue, ctx) => { - let message = ""; - - switch (issue.code) { - case z.ZodIssueCode.invalid_type: - if (issue.received === z.ZodParsedType.undefined) { - message = data.errors.invalid_type_received_undefined; - } else { - message = FormErrorTranslation(data.errors.invalid_type, { - expected: data.local_types[issue.expected], - received: data.local_types[issue.received], - }); - } - break; - case z.ZodIssueCode.invalid_literal: - message = FormErrorTranslation(data.errors.invalid_literal, { - expected: JSON.stringify(issue.expected, jsonStringifyReplacer), - }); - break; - case z.ZodIssueCode.unrecognized_keys: - message = FormErrorTranslation(data.errors.unrecognized_keys, { - keys: joinValues(issue.keys, ", "), - count: issue.keys.length, - }); - break; - case z.ZodIssueCode.invalid_union: - message = data.errors.invalid_union; - break; - case z.ZodIssueCode.invalid_union_discriminator: - message = FormErrorTranslation( - data.errors.invalid_union_discriminator, - { - options: joinValues(issue.options), - }, - ); - break; - case z.ZodIssueCode.invalid_enum_value: - message = FormErrorTranslation(data.errors.invalid_enum_value, { - options: joinValues(issue.options), - }); - break; - case z.ZodIssueCode.invalid_arguments: - message = data.errors.invalid_arguments; - break; - case z.ZodIssueCode.invalid_return_type: - message = data.errors.invalid_return_type; - break; - case z.ZodIssueCode.invalid_date: - message = data.errors.invalid_date; - break; - case z.ZodIssueCode.invalid_string: - if (typeof issue.validation === "object") { - if ("startsWith" in issue.validation) { - message = FormErrorTranslation( - data.errors.invalid_string.startsWith, - { - startsWith: issue.validation.startsWith, - }, - ); - } else if ("endsWith" in issue.validation) { - message = FormErrorTranslation( - data.errors.invalid_string.endsWith, - { - endsWith: issue.validation.endsWith, - }, - ); - } - } else { - message = FormErrorTranslation( - data.errors.invalid_string[ - issue.validation as keyof typeof data.errors.invalid_string - ], - { - validation: FormErrorTranslation( - data.validations[ - issue.validation as keyof typeof data.validations - ], - { - defaultValue: issue.validation, - }, - ), - }, - ); - } - break; - case z.ZodIssueCode.too_small: { - const minimum = - issue.type === "date" - ? new Date(issue.minimum as number) - : issue.minimum; - - const incl_excl = issue.exact - ? "exact" - : issue.inclusive - ? "inclusive" - : "not_inclusive"; - message = FormErrorTranslation( - data.errors.too_small[ - issue.type as keyof typeof data.errors.too_small - ][incl_excl], - { - minimum: minimum, - - //count: typeof minimum === "number" ? minimum : undefined, - }, - ); - - break; - } - case z.ZodIssueCode.too_big: { - const maximum = - issue.type === "date" - ? new Date(issue.maximum as number) - : issue.maximum; - message = FormErrorTranslation( - data.errors.too_big[issue.type as keyof typeof data.errors.too_big][ - issue.exact - ? "exact" - : issue.inclusive - ? "inclusive" - : "not_inclusive" - ], - { - maximum: maximum, - //count: typeof maximum === "number" ? maximum : undefined, - }, - ); - break; - } - - case z.ZodIssueCode.invalid_intersection_types: - message = data.errors.invalid_intersection_types; - break; - case z.ZodIssueCode.not_multiple_of: - message = FormErrorTranslation(data.errors.not_multiple_of, { - multipleOf: issue.multipleOf, - }); - break; - case z.ZodIssueCode.not_finite: - message = data.errors.not_finite; - break; - case z.ZodIssueCode.custom: - message = - data.customErrors[ - issue.params?.type as keyof typeof data.customErrors - ]; - break; - default: - message = ctx.defaultError; - } - return { message: message }; - }; - - return customErrorMap; -}; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const jsonStringifyReplacer = (_: string, value: any): any => { - if (typeof value === "bigint") { - return value.toString(); - } - return value; -}; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function joinValues(array: T, separator = " | "): string { - return ( - array - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - .map((val) => (typeof val === "string" ? `'${val}'` : val)) - .join(separator) - ); -} diff --git a/apps/infoalloggi/src/hooks/useStrongPassword.ts b/apps/infoalloggi/src/hooks/useStrongPassword.ts index f236b08..c87d48f 100644 --- a/apps/infoalloggi/src/hooks/useStrongPassword.ts +++ b/apps/infoalloggi/src/hooks/useStrongPassword.ts @@ -1,11 +1,18 @@ import { useState } from "react"; -import { z, type RefinementCtx } from "zod"; +import { z, type RefinementCtx } from "zod/v4"; import { useTranslation } from "~/providers/I18nProvider"; -export const zStrongPassword = z.string().superRefine((password) => { - return [/.{8,}/, /[0-9]/, /[a-z]/, /[A-Z]/].every((regex) => +export const zStrongPassword = z.string().superRefine((password, ctx) => { + const test = [/.{8,}/, /[0-9]/, /[a-z]/, /[A-Z]/].every((regex) => regex.test(password), ); + if (!test) { + ctx.addIssue({ + code: "custom", + message: + "La password deve contenere almeno 8 caratteri, un numero, una lettera minuscola e una maiuscola.", + }); + } }); export const useStrongPassword = () => { @@ -57,13 +64,14 @@ export const useStrongPassword = () => { const complexity = checkStrength(password); if (complexity.filter((req) => req.met).length !== complexity.length) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: complexity .filter((req) => !req.met) .map((req) => req.text) .join("\n"), path: [path], + input: "", }); } setStrengthScore(complexity.filter((req) => req.met).length); diff --git a/apps/infoalloggi/src/lib/form_utils.ts b/apps/infoalloggi/src/lib/form_utils.ts index 273337c..fba7138 100644 --- a/apps/infoalloggi/src/lib/form_utils.ts +++ b/apps/infoalloggi/src/lib/form_utils.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import type { z } from "zod/v4"; import { getComuneFromIndex } from "~/i18n/comuni"; import { searchNazioneByIndex } from "~/i18n/nazioni"; import type { ListOption } from "~/components/custom_ui/multiselect"; @@ -185,26 +185,29 @@ export const checkFiscalCodeValidity = ({ console.log("gen", gen); console.log("codiceFiscale", codiceFiscale); console.log("alternative", getAlternativeCF(gen)); - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Codice fiscale non valido", path: [path], + input: "", }); } if (control !== codiceFiscale.slice(-1)) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Codice di controllo non valido", path: [path], + input: "", }); } if (codiceFiscale.length !== 16) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci un codice fiscale valido", path: [path], + input: "", }); } if ( @@ -212,60 +215,67 @@ export const checkFiscalCodeValidity = ({ codiceFiscale, ) ) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Inserisci un codice fiscale valido", path: [path], + input: "", }); } if (codiceFiscale.slice(0, 3) !== getInizialiCF(cognomeUpper)) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Codice fiscale incompatibile con cognome inserito", path: [path], + input: "", }); } if (codiceFiscale.slice(3, 6) !== getInizialiCF(nomeUpper)) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Codice fiscale incompatibile con nome inserito", path: [path], + input: "", }); } if ( codiceFiscale.slice(6, 8) !== dataNascita.getFullYear().toString().slice(-2) ) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Codice fiscale incompatibile con anno di nascita", path: [path], + input: "", }); } if (codiceFiscale.slice(8, 9) !== getMonthLetterCF(dataNascita.getMonth())) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Codice fiscale incompatibile con mese di nascita", path: [path], + input: "", }); } if (codiceFiscale.slice(9, 11) !== getDayCF(dataNascita.getDate(), sesso)) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Codice fiscale incompatibile con giorno di nascita", path: [path], + input: "", }); } if (nazioneNascita === "0") { if (luogoNascita !== "") { const comune = getComuneFromIndex(parseInt(luogoNascita)); if (comune && codiceFiscale.slice(11, 15) !== comune.CATASTO) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Codice fiscale incompatibile con luogo di nascita", path: [path], + input: "", }); } } @@ -273,10 +283,11 @@ export const checkFiscalCodeValidity = ({ if (nazioneNascita !== "") { const nazione = searchNazioneByIndex(parseInt(nazioneNascita)); if (nazione && codiceFiscale.slice(11, 15) !== nazione.CATASTO) { - refinementContext.addIssue({ - code: z.ZodIssueCode.custom, + refinementContext.issues.push({ + code: "custom", message: "Codice fiscale incompatibile con luogo di nascita", path: [path], + input: "", }); } } diff --git a/apps/infoalloggi/src/lib/zodForm.ts b/apps/infoalloggi/src/lib/zodForm.ts index cda8fad..5aef058 100644 --- a/apps/infoalloggi/src/lib/zodForm.ts +++ b/apps/infoalloggi/src/lib/zodForm.ts @@ -1,16 +1,17 @@ import { zodResolver } from "@hookform/resolvers/zod"; -import { type UseFormProps, useForm } from "react-hook-form"; -import type { z, ZodTypeAny } from "zod"; +import { type FieldValues, type UseFormProps, useForm } from "react-hook-form"; -export function useZodForm( - props: Omit>, "resolver"> & { - schema: TSchema; - }, +import type { z } from "zod"; + +export function useZodForm< + T extends z.ZodType, + TContext, +>( + schema: T, + props?: Omit, TContext, z.output>, "resolver">, ) { - const form = useForm>({ + return useForm({ + resolver: zodResolver(schema), ...props, - resolver: zodResolver(props.schema), }); - - return form; } diff --git a/apps/infoalloggi/src/pages/area-riservata/admin/edit-annuncio/[id].tsx b/apps/infoalloggi/src/pages/area-riservata/admin/edit-annuncio/[id].tsx index a39c5fa..a7a0b37 100644 --- a/apps/infoalloggi/src/pages/area-riservata/admin/edit-annuncio/[id].tsx +++ b/apps/infoalloggi/src/pages/area-riservata/admin/edit-annuncio/[id].tsx @@ -21,7 +21,7 @@ import { cn } from "~/lib/utils"; import type { NextPageWithLayout } from "~/pages/_app"; import { generateSSGHelper } from "~/server/utils/ssgHelper"; import { api } from "~/utils/api"; -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -32,7 +32,6 @@ import { } from "~/components/custom_ui/form"; import { useZodForm } from "~/lib/zodForm"; import { useTranslation } from "~/providers/I18nProvider"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { CarouselAnnuncio } from "~/components/annuncio_card"; import { zAnnuncioId } from "~/server/utils/zod_types"; import toast from "react-hot-toast"; @@ -76,7 +75,7 @@ const AnnuncioEditForm = ({ data }: { data: AnnunciWithCoverImage }) => { desc_en: z.string().nonempty("Inserisci una descrizione valida"), status: z.string().nonempty("Seleziona uno stato"), locatore: z.string().nonempty("Inserisci il locatore"), - email: z.string().email("Inserisci un'email valida"), + email: z.email(), numero: z.string().nonempty("Inserisci un numero di telefono"), }); @@ -103,10 +102,9 @@ const AnnuncioEditForm = ({ data }: { data: AnnunciWithCoverImage }) => { numero: data.numero || "", }; - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); - const form = useZodForm({ - schema: EdiAnnuncioSchema, + const form = useZodForm(EdiAnnuncioSchema, { defaultValues: defaultValues, }); function onSubmit(fields: EditAnnuncioFormValues) { diff --git a/apps/infoalloggi/src/pages/contatti.tsx b/apps/infoalloggi/src/pages/contatti.tsx index c3bde6c..81213a9 100644 --- a/apps/infoalloggi/src/pages/contatti.tsx +++ b/apps/infoalloggi/src/pages/contatti.tsx @@ -1,7 +1,7 @@ import type { NextPage } from "next"; import Head from "next/head"; import Link from "next/link"; -import { z } from "zod"; +import { z } from "zod/v4"; import { Form, FormControl, @@ -12,7 +12,6 @@ import { } from "~/components/custom_ui/form"; import Input from "~/components/custom_ui/input"; import { Textarea } from "~/components/custom_ui/textarea"; -import { customErrorMapWrapper } from "~/hooks/locale"; import { useZodForm } from "~/lib/zodForm"; import { DualInputLayout } from "~/components/custom_ui/inputLayouts"; import { api } from "~/utils/api"; @@ -27,14 +26,13 @@ const Contatto: NextPage = () => { const ContactFormSchema = z.object({ nome: z.string().nonempty("Inserisci un nome valido"), cognome: z.string().nonempty("Inserisci un cognome valido"), - email: z.string().email(), + email: z.email(), messaggio: z.string().nonempty("Inserisci un messaggio"), numero: z.string().nonempty("Inserisci un numero di telefono"), checkbox: z.boolean().refine((val) => val === false), // Honeypot field }); - const form = useZodForm({ - schema: ContactFormSchema, + const form = useZodForm(ContactFormSchema, { defaultValues: { nome: "", cognome: "", @@ -60,7 +58,7 @@ const Contatto: NextPage = () => { telefono: data.numero, }); }; - z.setErrorMap(customErrorMapWrapper(locale)); + z.config(z.locales[locale]()); return ( <> diff --git a/apps/infoalloggi/src/server/api/routers/annunci.ts b/apps/infoalloggi/src/server/api/routers/annunci.ts index a08adbb..de5a754 100644 --- a/apps/infoalloggi/src/server/api/routers/annunci.ts +++ b/apps/infoalloggi/src/server/api/routers/annunci.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { adminProcedure, createTRPCRouter, diff --git a/apps/infoalloggi/src/server/api/routers/auth.ts b/apps/infoalloggi/src/server/api/routers/auth.ts index edb7080..5c88803 100644 --- a/apps/infoalloggi/src/server/api/routers/auth.ts +++ b/apps/infoalloggi/src/server/api/routers/auth.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { adminProcedure, createTRPCRouter, @@ -36,7 +36,7 @@ export const authRouter = createTRPCRouter({ loginUser: publicProcedure .input( z.object({ - email: z.string().email("Inserisci una email valida"), + email: z.email(), password: z.string(), }), ) @@ -68,7 +68,7 @@ export const authRouter = createTRPCRouter({ addUserAdmin: adminProcedure .input( z.object({ - email: z.string().email("Inserisci una email valida"), + email: z.email(), password: zStrongPassword, nome: z.string().nonempty("Inserisci un nome valido"), cognome: z.string().nonempty("Inserisci un cognome valido"), diff --git a/apps/infoalloggi/src/server/api/routers/banlist.ts b/apps/infoalloggi/src/server/api/routers/banlist.ts index d0cdeca..3790000 100644 --- a/apps/infoalloggi/src/server/api/routers/banlist.ts +++ b/apps/infoalloggi/src/server/api/routers/banlist.ts @@ -5,7 +5,7 @@ import { updateBan, } from "~/server/services/banlist.service"; import { adminProcedure, createTRPCRouter } from "~/server/api/trpc"; -import { z } from "zod"; +import { z } from "zod/v4"; import { zBanlistId, zBanType } from "~/server/utils/zod_types"; import { db } from "~/server/db"; diff --git a/apps/infoalloggi/src/server/api/routers/chat.ts b/apps/infoalloggi/src/server/api/routers/chat.ts index e903315..44d4a00 100644 --- a/apps/infoalloggi/src/server/api/routers/chat.ts +++ b/apps/infoalloggi/src/server/api/routers/chat.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { adminProcedure, createTRPCRouter, diff --git a/apps/infoalloggi/src/server/api/routers/chat_sse.ts b/apps/infoalloggi/src/server/api/routers/chat_sse.ts index 167c672..5e044cd 100644 --- a/apps/infoalloggi/src/server/api/routers/chat_sse.ts +++ b/apps/infoalloggi/src/server/api/routers/chat_sse.ts @@ -1,5 +1,5 @@ import { db } from "~/server/db"; -import { z } from "zod"; +import { z } from "zod/v4"; import { createTRPCRouter, protectedProcedure, diff --git a/apps/infoalloggi/src/server/api/routers/comunicazioni.ts b/apps/infoalloggi/src/server/api/routers/comunicazioni.ts index 743cf4f..d7fa74c 100644 --- a/apps/infoalloggi/src/server/api/routers/comunicazioni.ts +++ b/apps/infoalloggi/src/server/api/routers/comunicazioni.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { createTRPCRouter, publicProcedure } from "~/server/api/trpc"; import { zUserId } from "~/server/utils/zod_types"; import { genMail, type MailsTemplates } from "~/server/services/mailer"; diff --git a/apps/infoalloggi/src/server/api/routers/contact.ts b/apps/infoalloggi/src/server/api/routers/contact.ts index 90ba9a2..a58442c 100644 --- a/apps/infoalloggi/src/server/api/routers/contact.ts +++ b/apps/infoalloggi/src/server/api/routers/contact.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { createTRPCRouter, publicProcedure } from "~/server/api/trpc"; import { NewMail } from "~/server/services/mailer"; // Create a new ratelimiter, that allows 10 requests per 10 seconds @@ -15,7 +15,7 @@ export const contactRouter = createTRPCRouter({ z.object({ nome: z.string(), cognome: z.string(), - email: z.string().email(), + email: z.email(), telefono: z.string(), messaggio: z.string(), }), diff --git a/apps/infoalloggi/src/server/api/routers/event_queue.ts b/apps/infoalloggi/src/server/api/routers/event_queue.ts index 3cc4ec5..d61244d 100644 --- a/apps/infoalloggi/src/server/api/routers/event_queue.ts +++ b/apps/infoalloggi/src/server/api/routers/event_queue.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { createTRPCRouter, protectedApiProcedure, diff --git a/apps/infoalloggi/src/server/api/routers/fatture.ts b/apps/infoalloggi/src/server/api/routers/fatture.ts index 46c2d92..ab301e7 100644 --- a/apps/infoalloggi/src/server/api/routers/fatture.ts +++ b/apps/infoalloggi/src/server/api/routers/fatture.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { adminProcedure, diff --git a/apps/infoalloggi/src/server/api/routers/interests.ts b/apps/infoalloggi/src/server/api/routers/interests.ts index 0d9ecd5..e6d47c4 100644 --- a/apps/infoalloggi/src/server/api/routers/interests.ts +++ b/apps/infoalloggi/src/server/api/routers/interests.ts @@ -5,7 +5,7 @@ import { RemoveInterest, } from "~/server/services/interests.service"; import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc"; -import z from "zod"; +import z from "zod/v4"; import { zAnnuncioId, zUserId } from "~/server/utils/zod_types"; import { NewMail } from "~/server/services/mailer"; import { findUser_byId_MINI } from "~/server/services/user.service"; diff --git a/apps/infoalloggi/src/server/api/routers/messages_sse.ts b/apps/infoalloggi/src/server/api/routers/messages_sse.ts index 469a938..f2ba09a 100644 --- a/apps/infoalloggi/src/server/api/routers/messages_sse.ts +++ b/apps/infoalloggi/src/server/api/routers/messages_sse.ts @@ -1,6 +1,6 @@ import { tracked, TRPCError } from "@trpc/server"; import { db } from "~/server/db"; -import { z } from "zod"; +import { z } from "zod/v4"; import { adminProcedure, createTRPCRouter, diff --git a/apps/infoalloggi/src/server/api/routers/pagamenti.ts b/apps/infoalloggi/src/server/api/routers/pagamenti.ts index f0f0891..455ca10 100644 --- a/apps/infoalloggi/src/server/api/routers/pagamenti.ts +++ b/apps/infoalloggi/src/server/api/routers/pagamenti.ts @@ -1,5 +1,5 @@ import { TRPCError } from "@trpc/server"; -import { z } from "zod"; +import { z } from "zod/v4"; import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc"; import { db } from "~/server/db"; // Create a new ratelimiter, that allows 10 requests per 10 seconds diff --git a/apps/infoalloggi/src/server/api/routers/prezziario.ts b/apps/infoalloggi/src/server/api/routers/prezziario.ts index e5dc327..adfdf75 100644 --- a/apps/infoalloggi/src/server/api/routers/prezziario.ts +++ b/apps/infoalloggi/src/server/api/routers/prezziario.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import type { NewPrezziario, PrezziarioUpdate, diff --git a/apps/infoalloggi/src/server/api/routers/servizio.ts b/apps/infoalloggi/src/server/api/routers/servizio.ts index 040660d..652e790 100644 --- a/apps/infoalloggi/src/server/api/routers/servizio.ts +++ b/apps/infoalloggi/src/server/api/routers/servizio.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { adminProcedure, createTRPCRouter, diff --git a/apps/infoalloggi/src/server/api/routers/settings.ts b/apps/infoalloggi/src/server/api/routers/settings.ts index a161855..bb5fca2 100644 --- a/apps/infoalloggi/src/server/api/routers/settings.ts +++ b/apps/infoalloggi/src/server/api/routers/settings.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import type { BannersIdbanner } from "~/schemas/public/Banners"; import { adminProcedure, diff --git a/apps/infoalloggi/src/server/api/routers/storage.ts b/apps/infoalloggi/src/server/api/routers/storage.ts index 041b403..7853604 100644 --- a/apps/infoalloggi/src/server/api/routers/storage.ts +++ b/apps/infoalloggi/src/server/api/routers/storage.ts @@ -1,5 +1,5 @@ import { TRPCError } from "@trpc/server"; -import { z } from "zod"; +import { z } from "zod/v4"; import { adminProcedure, createTRPCRouter, diff --git a/apps/infoalloggi/src/server/api/routers/stripe.ts b/apps/infoalloggi/src/server/api/routers/stripe.ts index 73c8ef2..6e11ade 100644 --- a/apps/infoalloggi/src/server/api/routers/stripe.ts +++ b/apps/infoalloggi/src/server/api/routers/stripe.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { createTRPCRouter, protectedProcedure, diff --git a/apps/infoalloggi/src/server/api/routers/users.ts b/apps/infoalloggi/src/server/api/routers/users.ts index 3d6acfe..23f9e9b 100644 --- a/apps/infoalloggi/src/server/api/routers/users.ts +++ b/apps/infoalloggi/src/server/api/routers/users.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { adminProcedure, createTRPCRouter, @@ -78,7 +78,7 @@ export const usersRouter = createTRPCRouter({ nome: z.string(), cognome: z.string(), telefono: z.string(), - email: z.string().email(), + email: z.email(), isAdmin: z.boolean().optional(), }), ) diff --git a/apps/infoalloggi/src/server/api/trpc.ts b/apps/infoalloggi/src/server/api/trpc.ts index 3d28ba5..cbc7cbf 100644 --- a/apps/infoalloggi/src/server/api/trpc.ts +++ b/apps/infoalloggi/src/server/api/trpc.ts @@ -11,7 +11,7 @@ import { initTRPC, TRPCError } from "@trpc/server"; import type { CreateNextContextOptions } from "@trpc/server/adapters/next"; import superjson from "superjson"; -import z, { ZodError } from "zod"; +import z, { ZodError } from "zod/v4"; import type { NextApiRequest, NextApiResponse } from "next"; import { env } from "~/env.mjs"; @@ -28,7 +28,7 @@ import { ACCESS_TOKEN_COOKIE_NAME, verifyToken } from "~/server/auth/jwt"; export const sessionSchema = z.object({ id: zUserId, username: z.string(), - email: z.string().email(), + email: z.email(), isAdmin: z.boolean(), isBlocked: z.boolean(), nome: z.string(), @@ -89,7 +89,7 @@ export const t = initTRPC.context().create({ data: { ...shape.data, zodError: - error.cause instanceof ZodError ? error.cause.flatten() : null, + error.cause instanceof ZodError ? z.treeifyError(error.cause) : null, }, }; }, diff --git a/apps/infoalloggi/src/server/controllers/annunci.controller.ts b/apps/infoalloggi/src/server/controllers/annunci.controller.ts index a8abbce..cd783aa 100644 --- a/apps/infoalloggi/src/server/controllers/annunci.controller.ts +++ b/apps/infoalloggi/src/server/controllers/annunci.controller.ts @@ -3,7 +3,7 @@ import { TRPCError } from "@trpc/server"; import { createSrc, createSrcset } from "~/server/services/imageServer"; import { AnnuncioObjectWithImages } from "~/server/services/annunci.service"; import { zAnnuncioId } from "~/server/utils/zod_types"; -import { z } from "zod"; +import { z } from "zod/v4"; import { db } from "~/server/db"; import type { ServizioServizioId } from "~/schemas/public/Servizio"; diff --git a/apps/infoalloggi/src/server/controllers/servizio.controller.ts b/apps/infoalloggi/src/server/controllers/servizio.controller.ts index 03c382a..114f134 100644 --- a/apps/infoalloggi/src/server/controllers/servizio.controller.ts +++ b/apps/infoalloggi/src/server/controllers/servizio.controller.ts @@ -10,7 +10,7 @@ import type { UsersAnagraficaUpdate } from "~/schemas/public/UsersAnagrafica"; import { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/postgres"; import type { AnnunciId } from "~/schemas/public/Annunci"; import { add } from "date-fns"; -import { z } from "zod"; +import { z } from "zod/v4"; import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; import { NewMail, type NewMailProps } from "~/server/services/mailer"; import type { ServizioAnnunciUpdate } from "~/schemas/public/ServizioAnnunci"; diff --git a/apps/infoalloggi/src/server/services/fic.service.ts b/apps/infoalloggi/src/server/services/fic.service.ts index c16604c..659819b 100644 --- a/apps/infoalloggi/src/server/services/fic.service.ts +++ b/apps/infoalloggi/src/server/services/fic.service.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; const ClientTypeSchema = z.enum(["company", "person", "pa", "condo"]); diff --git a/apps/infoalloggi/src/server/utils/sse_zod.ts b/apps/infoalloggi/src/server/utils/sse_zod.ts index 37ad426..a88c0d8 100644 --- a/apps/infoalloggi/src/server/utils/sse_zod.ts +++ b/apps/infoalloggi/src/server/utils/sse_zod.ts @@ -16,64 +16,43 @@ const trackedEnvelopeSchema = * 3. The return value of the async iterable, if any, also conforms to a specified type. */ export function zAsyncIterable< - TYieldIn, - TYieldOut, - TReturnIn = void, - TReturnOut = void, + TYield, + TReturn = void, Tracked extends boolean = false, >(opts: { - /** - * Validate the value yielded by the async generator - */ - yield: z.ZodType; - /** - * Validate the return value of the async generator - * @remark not applicable for subscriptions - */ - return?: z.ZodType; - /** - * Whether if the yielded values are tracked - * @remark only applicable for subscriptions - */ + yield: z.ZodType; + return?: z.ZodType; tracked?: Tracked; }) { - return z - .custom< - AsyncIterable< - Tracked extends true ? TrackedEnvelope : TYieldIn, - TReturnIn - > - >((val) => isAsyncIterable(val)) - .transform(async function* (iter) { - const iterator = iter[Symbol.asyncIterator](); - try { - let next; - while ((next = await iterator.next()) && !next.done) { - if (opts.tracked) { - const [id, data] = trackedEnvelopeSchema.parse(next.value); - yield tracked(id, await opts.yield.parseAsync(data)); - continue; - } - yield opts.yield.parseAsync(next.value); - } - if (opts.return) { - return await opts.return.parseAsync(next.value); - } - return; - } finally { - await iterator.return?.(); - } - }) as z.ZodType< + const baseSchema = z.custom< AsyncIterable< - Tracked extends true ? TrackedEnvelope : TYieldIn, - TReturnIn, - unknown - >, - any, - AsyncIterable< - Tracked extends true ? TrackedEnvelope : TYieldOut, - TReturnOut, - unknown + Tracked extends true + ? TrackedEnvelope> + : z.input, + z.input > - >; + >((val: unknown): val is AsyncIterable => isAsyncIterable(val), { + message: "Expected AsyncIterable", + }); + + return baseSchema.transform(async function* (iter) { + const iterator = iter[Symbol.asyncIterator](); + try { + let next; + while ((next = await iterator.next()) && !next.done) { + if (opts.tracked) { + const [id, data] = await trackedEnvelopeSchema.parseAsync(next.value); + yield tracked(id, await opts.yield.parseAsync(data)); + continue; + } + yield await opts.yield.parseAsync(next.value); + } + if (opts.return) { + return (await opts.return.parseAsync(next.value)) as TReturn; + } + return; + } finally { + await iterator.return?.(); + } + }); } diff --git a/apps/infoalloggi/src/server/utils/zod_types.ts b/apps/infoalloggi/src/server/utils/zod_types.ts index c0ba509..03244e6 100644 --- a/apps/infoalloggi/src/server/utils/zod_types.ts +++ b/apps/infoalloggi/src/server/utils/zod_types.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import type { AnnunciId } from "~/schemas/public/Annunci"; import type { ChatsChatid } from "~/schemas/public/Chats"; import type { EtichetteIdEtichetta } from "~/schemas/public/Etichette"; @@ -75,10 +75,9 @@ export const zBanlistId = z.custom( "falied to validate BanlistId", ); -export const zBanType = z.custom( - (val) => val in BanType, - "falied to validate BanType", -); +export const zBanType = z.enum(BanType, { + error: "failed to validate BanType", +}); export const zServizioId = z.custom( (val) => typeof val === "string",