diff --git a/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx b/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx index 311dd1f..5b58086 100644 --- a/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx +++ b/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx @@ -1,6 +1,7 @@ import { differenceInYears, format } from "date-fns"; import { enUS, it } from "date-fns/locale"; import { CalendarIcon } from "lucide-react"; +import dynamic from "next/dynamic"; import { type Control, useWatch } from "react-hook-form"; import toast from "react-hot-toast"; import { z } from "zod/v4"; @@ -22,6 +23,7 @@ import { PopoverTrigger, } from "~/components/ui/popover"; import { Separator } from "~/components/ui/separator"; +import { Switch } from "~/components/ui/switch"; import { getProvinciaFromSigla, ITALY_CATASTO_CODE, @@ -36,32 +38,35 @@ import { cn } from "~/lib/utils"; import { useZodForm } from "~/lib/zodForm"; import { useCatasto } from "~/providers/CatastoProvider"; import { useTranslation } from "~/providers/I18nProvider"; +import { UsersAnagraficaSchema } from "~/schemas/public/UsersAnagrafica"; import type { CompleteUserData } from "~/server/services/user.service"; import { api } from "~/utils/api"; -const profileFormSchema = z.object({ - cap_residenza: z.string().optional(), - civico_residenza: z.string().optional(), - codice_fiscale: z.string().optional(), - comune_residenza: z.string().nullable(), - data_nascita: z.date().optional(), - luogo_nascita: z.string().nullable(), - nazione_nascita: z.string().nullable(), - nazione_residenza: z.string().nullable(), - provincia_residenza: z.string().nullable(), - sesso: z.string().optional(), - via_residenza: z.string().optional(), -}); +const DevT: React.ElementType = dynamic( + () => import("@hookform/devtools").then((module) => module.DevTool), + { ssr: false }, +); -type ProfileFormValues = z.infer; +const Schema = UsersAnagraficaSchema.omit({ + idanagrafica: true, + userid: true, + doc_personale_fronte_ref: true, + doc_personale_retro_ref: true, +}) + .extend({ + admin_override: z.boolean(), + }) + .nonoptional(); +type ProfileFormValues = z.infer; export const ProfileFormAnagrafica = ({ userData, + isAdmin, }: { userData: CompleteUserData; + isAdmin: boolean; }) => { const { comuni, nazioni } = useCatasto(); - - const schema = profileFormSchema.superRefine( + const schema = Schema.superRefine( ( { data_nascita, @@ -70,9 +75,11 @@ export const ProfileFormAnagrafica = ({ luogo_nascita, nazione_nascita, cap_residenza, + admin_override, }, refinementContext, ) => { + if (admin_override) return; if (cap_residenza) { if (cap_residenza.length !== 5) { refinementContext.issues.push({ @@ -131,13 +138,14 @@ export const ProfileFormAnagrafica = ({ provincia_residenza: userData.provincia_residenza, sesso: userData.sesso || "", via_residenza: userData.via_residenza || "", + admin_override: false, }; const { locale, t } = useTranslation(); const DatePickerLocale = locale === "it" ? it : enUS; z.config(z.locales[locale]()); const form = useZodForm(schema, { - defaultValues: defaultValues, + defaultValues, }); const utils = api.useUtils(); @@ -166,7 +174,7 @@ export const ProfileFormAnagrafica = ({ return ( <>
- {/* */} + @@ -400,6 +408,7 @@ export const ProfileFormAnagrafica = ({ await form.trigger("codice_fiscale"); }} type="text" + value={field.value || undefined} /> @@ -414,8 +423,36 @@ export const ProfileFormAnagrafica = ({ setValue={form.setValue} trigger={form.trigger} /> - - +
+ + {isAdmin && ( + ( + +
+ + Abilita modifiche senza controlli (admin) + + + { + field.onChange(v); + }} + /> + + +
+
+ )} + /> + )} +
@@ -539,6 +576,11 @@ const ResidenzaSection = ({ { + field.onChange( + e.target.value === "" ? null : e.target.value, + ); + }} type="text" value={field.value || undefined} /> @@ -621,7 +663,9 @@ const ResidenzaSection = ({ className="w-full" id="comune_residenza" onChange={async (v) => { - field.onChange(v.target.value); + field.onChange( + v.target.value !== "" ? v.target.value : null, + ); await trigger("comune_residenza"); }} type="text" @@ -647,8 +691,14 @@ const ResidenzaSection = ({ { + field.onChange( + e.target.value === "" ? null : e.target.value, + ); + }} placeholder="" type="text" + value={field.value || undefined} /> @@ -671,8 +721,14 @@ const ResidenzaSection = ({ { + field.onChange( + e.target.value === "" ? null : e.target.value, + ); + }} placeholder="Via ..." type="text" + value={field.value || undefined} /> @@ -695,8 +751,14 @@ const ResidenzaSection = ({ { + field.onChange( + e.target.value === "" ? null : e.target.value, + ); + }} placeholder="" type="text" + value={field.value || undefined} /> diff --git a/apps/infoalloggi/src/pages/area-riservata/admin/user-view/edit-user/[userId].tsx b/apps/infoalloggi/src/pages/area-riservata/admin/user-view/edit-user/[userId].tsx index 6aec9f6..d8e8fdb 100644 --- a/apps/infoalloggi/src/pages/area-riservata/admin/user-view/edit-user/[userId].tsx +++ b/apps/infoalloggi/src/pages/area-riservata/admin/user-view/edit-user/[userId].tsx @@ -208,7 +208,7 @@ const EditUser: NextPageWithLayout = ({ - + diff --git a/apps/infoalloggi/src/pages/area-riservata/profilo.tsx b/apps/infoalloggi/src/pages/area-riservata/profilo.tsx index f337965..6ad5dd1 100644 --- a/apps/infoalloggi/src/pages/area-riservata/profilo.tsx +++ b/apps/infoalloggi/src/pages/area-riservata/profilo.tsx @@ -108,7 +108,10 @@ const Dashboard: NextPageWithLayout = () => { - + diff --git a/apps/infoalloggi/src/server/utils/zod_utils.ts b/apps/infoalloggi/src/server/utils/zod_utils.ts index 348c424..7691fa0 100644 --- a/apps/infoalloggi/src/server/utils/zod_utils.ts +++ b/apps/infoalloggi/src/server/utils/zod_utils.ts @@ -49,4 +49,41 @@ export const MessageUpdateEventSchema = z.discriminatedUnion("eventType", [ z.object({ eventType: z.literal("read_receipt"), data: z.null() }), // all unread messages in this chat are now read ]); +type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; +/** + * The "Diff" engine that identifies specific mismatches + */ +type SchemaDiff = Expand<{ + [K in keyof T | keyof U]: K extends keyof T + ? K extends keyof U + ? T[K] extends U[K] + ? U[K] extends T[K] + ? never + : { mismatch: "Type structural difference"; s1: T[K]; s2: U[K] } + : { schema1: T[K]; schema2: U[K] } + : { MISSING_IN: "Schema2"; value: T[K] } + : K extends keyof U + ? { MISSING_IN: "Schema1"; value: U[K] } + : never; +}>; + +/** + * @public + * The Helper Function + * If the types match perfectly, it returns true. + * If they don't, it returns the Diff object as a type error. + * @example const result = assertSchemasEqual(S1, S2); + */ +export function assertSchemasEqual< + S1 extends z.ZodTypeAny, + S2 extends z.ZodTypeAny, +>( + _s1: S1, + _s2: S2, +): [SchemaDiff, z.infer>] extends [Record] + ? "✅ Schemas Match" + : { ERROR: "Schemas Diverge"; DIFF: SchemaDiff, z.infer> } { + // biome-ignore lint/suspicious/noExplicitAny: + return "✅ Schemas Match" as any; +}