diff --git a/apps/infoalloggi/src/components/Layout.tsx b/apps/infoalloggi/src/components/Layout.tsx
index a16821e..1d700ec 100644
--- a/apps/infoalloggi/src/components/Layout.tsx
+++ b/apps/infoalloggi/src/components/Layout.tsx
@@ -1,20 +1,6 @@
import { getCookie } from "cookies-next";
-import {
- ExternalLink,
- Mail,
- MessagesSquare,
- Paperclip,
- Search,
- ShoppingBag,
- Tickets,
- UserCog,
-} from "lucide-react";
import Head from "next/head";
-import Link from "next/link";
-import { usePathname } from "next/navigation";
-import { useRouter } from "next/router";
-import { type ReactNode, useContext } from "react";
-import toast from "react-hot-toast";
+import type { ReactNode } from "react";
import {
SIDEBAR_COOKIE_NAME,
Sidebar,
@@ -23,10 +9,7 @@ import { BannerFactory } from "~/components/banners";
import { Footer, MiniFooter } from "~/components/footer";
import { LoadingPage } from "~/components/loading";
import { SiteHeader } from "~/components/navbar/site-header";
-import { WhatsAppIcon2 } from "~/components/svgs";
-import { Button } from "~/components/ui/button";
-import { Separator } from "~/components/ui/separator";
-import { UserViewContext, UserViewProvider } from "~/lib/userViewContext";
+import { UserViewProvider } from "~/lib/userViewContext";
import { cn } from "~/lib/utils";
import { useTranslation } from "~/providers/I18nProvider";
import {
@@ -36,6 +19,7 @@ import {
import type { UsersId } from "~/schemas/public/Users";
import type { ValidSession } from "~/server/api/trpc";
import { api } from "~/utils/api";
+import { UserViewHeader } from "./area-riservata/userViewHeader";
interface Props {
bodyClassName?: string;
@@ -68,7 +52,6 @@ export const Layout = ({
{!noFooter && }
- {/* */}
);
};
@@ -171,8 +154,6 @@ export const AreaRiservataLayout = ({
{!noFooter && }
-
- {/* */}
);
};
@@ -214,132 +195,6 @@ export const AreaRiservataLayoutUserView = ({
-
- {/* */}
-
- );
-};
-
-const UserViewHeader = () => {
- const data = useContext(UserViewContext);
- const pathname = usePathname();
- const router = useRouter();
- const { mutate: swap } = api.auth.swapUser.useMutation({
- onSuccess: async () => {
- toast.success("Utente cambiato con successo");
- await router.push("/area-riservata/dashboard");
- router.reload();
- },
- });
- if (!data) return
Errore
;
- return (
-
-
-
{data.username}
-
- {data.email} | {data.telefono}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
);
};
diff --git a/apps/infoalloggi/src/components/area-riservata/documenti_personali.tsx b/apps/infoalloggi/src/components/area-riservata/documenti_personali.tsx
index a9157d8..8619ab2 100644
--- a/apps/infoalloggi/src/components/area-riservata/documenti_personali.tsx
+++ b/apps/infoalloggi/src/components/area-riservata/documenti_personali.tsx
@@ -4,7 +4,7 @@ import toast from "react-hot-toast";
import { useSession } from "~/providers/SessionProvider";
import type { UsersId } from "~/schemas/public/Users";
import type { UsersStorageUserStorageId } from "~/schemas/public/UsersStorage";
-import type { UserWithAnagrafica } from "~/server/services/user.service";
+import type { CompleteUserData } from "~/server/services/user.service";
import { api } from "~/utils/api";
import { Confirm } from "../confirm";
import { ImageFlbk } from "../ImageWithFallback";
@@ -24,7 +24,7 @@ import { UploadModal } from "../upload_modal";
export const DocumentiPersonali = ({
userData,
}: {
- userData: UserWithAnagrafica;
+ userData: CompleteUserData;
}) => {
const utils = api.useUtils();
const { mutate, isPending } = api.users.editUserAnagrafica.useMutation({
diff --git a/apps/infoalloggi/src/components/area-riservata/userViewHeader.tsx b/apps/infoalloggi/src/components/area-riservata/userViewHeader.tsx
new file mode 100644
index 0000000..3856c62
--- /dev/null
+++ b/apps/infoalloggi/src/components/area-riservata/userViewHeader.tsx
@@ -0,0 +1,250 @@
+import { format } from "date-fns";
+import {
+ ExternalLink,
+ Mail,
+ MessagesSquare,
+ Paperclip,
+ Search,
+ ShoppingBag,
+ Tickets,
+ UserCog,
+ UserPen,
+} from "lucide-react";
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import { useRouter } from "next/router";
+import toast from "react-hot-toast";
+import { useUserViewContext } from "~/lib/userViewContext";
+import { api } from "~/utils/api";
+import { WhatsAppIcon2 } from "../svgs";
+import { Button } from "../ui/button";
+import { Separator } from "../ui/separator";
+
+export const UserViewHeader = () => {
+ const data = useUserViewContext();
+ const pathname = usePathname();
+ const router = useRouter();
+ const { mutate: swap } = api.auth.swapUser.useMutation({
+ onSuccess: async () => {
+ toast.success("Utente cambiato con successo");
+ await router.push("/area-riservata/dashboard");
+ router.reload();
+ },
+ });
+ if (!data) return Errore
;
+ return (
+
+
+
{data.username}
+
+
+
+
+ {data.codice_fiscale ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const VOCALI = ["A", "E", "I", "O", "U"] as const;
+
+const IntestazioneMaker = () => {
+ const data = useUserViewContext();
+ const { data: comuni = [] } = api.catasto.searchComuniByCatasto.useQuery({
+ catasto: [data?.luogo_nascita || "", data?.comune_residenza || ""].filter(
+ (f) => f !== "",
+ ),
+ });
+
+ const isDisabled =
+ !data.sesso ||
+ !data.cognome ||
+ !data.nome ||
+ !data.data_nascita ||
+ !data.luogo_nascita ||
+ !data.comune_residenza ||
+ !data.via_residenza ||
+ !data.civico_residenza;
+
+ const copyToClipboard = () => {
+ if (
+ !data.sesso ||
+ !data.cognome ||
+ !data.nome ||
+ !data.data_nascita ||
+ !data.luogo_nascita ||
+ !data.comune_residenza ||
+ !data.via_residenza ||
+ !data.civico_residenza
+ ) {
+ toast.error("Dati mancanti");
+ return;
+ }
+
+ const comune_nascita = comuni.find((c) => c.catasto === data.luogo_nascita);
+ const comune_residenza = comuni.find(
+ (c) => c.catasto === data.comune_residenza,
+ );
+ if (!comune_nascita || !comune_residenza) {
+ toast.error("Errore comune non trovato");
+ return;
+ }
+
+ // Format names
+ const cognome = titleCase(data.cognome);
+ const nome = titleCase(data.nome);
+ const viaResidenza = titleCase(data.via_residenza);
+ const comuneNascita = titleCase(comune_nascita.nome);
+ const comuneResidenza = titleCase(comune_residenza.nome);
+
+ // Determine article
+ const isMale = data.sesso === "M";
+ const articolo = isMale ? "Al Sig." : "Alla Sig.ra";
+ const natoNata = isMale ? "nato" : "nata";
+ const preposizione = VOCALI.includes(comuneNascita[0]?.toUpperCase() || "")
+ ? "ad"
+ : "a";
+
+ // Build phrase
+ const frase = [
+ `${articolo} ${cognome} ${nome}`,
+ `${natoNata} il ${format(data.data_nascita, "dd/MM/yyyy")}`,
+ `${preposizione} ${comuneNascita} (${comune_nascita.sigla}),`,
+ `residente in ${viaResidenza}, ${data.civico_residenza}`,
+ `a ${comuneResidenza} (${data.provincia_residenza})`,
+ `CAP ${data.cap_residenza},`,
+ `con codice fiscale: ${data.codice_fiscale}`,
+ ].join(" ");
+
+ navigator.clipboard
+ .writeText(frase)
+ .then(() => {
+ toast.success("Intestazione copiata negli appunti");
+ })
+ .catch(() => {
+ toast.error("Errore nella copia");
+ });
+ };
+
+ return (
+
+ );
+};
+
+function titleCase(str: string | null | undefined): string {
+ if (!str) return "";
+
+ return str
+ .trim()
+ .toLowerCase()
+ .split(/\s+/) // Handle multiple spaces
+ .map((word) => {
+ if (word.length === 0) return word;
+ return word.charAt(0).toUpperCase() + word.slice(1);
+ })
+ .join(" ");
+}
diff --git a/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx b/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx
index 10012ce..e1b9ca8 100644
--- a/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx
+++ b/apps/infoalloggi/src/forms/FormProfilo_Anagrafica.tsx
@@ -34,7 +34,7 @@ import { cn } from "~/lib/utils";
import { useZodForm } from "~/lib/zodForm";
import { useCatasto } from "~/providers/CatastoProvider";
import { useTranslation } from "~/providers/I18nProvider";
-import type { UserWithAnagrafica } from "~/server/services/user.service";
+import type { CompleteUserData } from "~/server/services/user.service";
import { api } from "~/utils/api";
const profileFormSchema = z.object({
@@ -62,7 +62,7 @@ type ProfileFormValues = z.infer;
export const ProfileFormAnagrafica = ({
userData,
}: {
- userData: UserWithAnagrafica;
+ userData: CompleteUserData;
}) => {
const { comuni, nazioni } = useCatasto();
diff --git a/apps/infoalloggi/src/lib/userViewContext.tsx b/apps/infoalloggi/src/lib/userViewContext.tsx
index 71f27d2..b555024 100644
--- a/apps/infoalloggi/src/lib/userViewContext.tsx
+++ b/apps/infoalloggi/src/lib/userViewContext.tsx
@@ -1,11 +1,11 @@
-import { createContext, type FC, type ReactNode } from "react";
+import { createContext, type FC, type ReactNode, useContext } from "react";
import type { ChatsChatid } from "~/schemas/public/Chats";
import type { UsersId } from "~/schemas/public/Users";
-import type { getUserReturnType } from "~/server/controllers/user.controller";
+import type { UserWAnagrafica } from "~/server/controllers/user.controller";
import { api } from "~/utils/api";
-type UserViewContextType = getUserReturnType & { chatid: ChatsChatid | null };
+type UserViewContextType = UserWAnagrafica & { chatid: ChatsChatid | null };
export const UserViewContext = createContext(null);
@@ -28,3 +28,13 @@ export const UserViewProvider: FC<{ children: ReactNode; userId: UsersId }> = ({
}
return null;
};
+
+export const useUserViewContext = () => {
+ const context = useContext(UserViewContext);
+ if (!context) {
+ throw new Error(
+ "useUserViewContext must be used within a UserViewProvider",
+ );
+ }
+ return context;
+};
diff --git a/apps/infoalloggi/src/server/api/routers/catasto.ts b/apps/infoalloggi/src/server/api/routers/catasto.ts
index 6bc891a..60c294c 100644
--- a/apps/infoalloggi/src/server/api/routers/catasto.ts
+++ b/apps/infoalloggi/src/server/api/routers/catasto.ts
@@ -6,6 +6,7 @@ import {
getNazioneById,
getNazioni,
getProvincie,
+ searchComuniByCatasto,
} from "~/server/controllers/catasto.controller";
import { zComuneId, zNazioneId } from "~/server/utils/zod_types";
@@ -14,6 +15,15 @@ export const catastoRouter = createTRPCRouter({
getComuni: publicProcedure.query(async () => {
return await getComuni();
}),
+ searchComuniByCatasto: publicProcedure
+ .input(
+ z.object({
+ catasto: z.string().array(),
+ }),
+ )
+ .query(async ({ input }) => {
+ return await searchComuniByCatasto(input.catasto);
+ }),
getComuneById: publicProcedure
.input(
z.object({
diff --git a/apps/infoalloggi/src/server/api/routers/users.ts b/apps/infoalloggi/src/server/api/routers/users.ts
index e91ac8d..c63cda8 100644
--- a/apps/infoalloggi/src/server/api/routers/users.ts
+++ b/apps/infoalloggi/src/server/api/routers/users.ts
@@ -15,7 +15,7 @@ import {
getUsersWithChatInfoHandler,
} from "~/server/controllers/user.controller";
import { db } from "~/server/db";
-import { getUserWithAnagraficaHandler } from "~/server/services/user.service";
+import { getClientProfilo } from "~/server/services/user.service";
import { zUserId } from "~/server/utils/zod_types";
export const usersRouter = createTRPCRouter({
@@ -71,7 +71,7 @@ export const usersRouter = createTRPCRouter({
}),
)
.query(async ({ input }) => {
- return getUserWithAnagraficaHandler({
+ return getClientProfilo({
db,
userId: input.userId,
});
diff --git a/apps/infoalloggi/src/server/controllers/catasto.controller.ts b/apps/infoalloggi/src/server/controllers/catasto.controller.ts
index dc9c2b5..0d602cf 100644
--- a/apps/infoalloggi/src/server/controllers/catasto.controller.ts
+++ b/apps/infoalloggi/src/server/controllers/catasto.controller.ts
@@ -15,6 +15,20 @@ export const getComuni = async () => {
}
};
+export const searchComuniByCatasto = async (catasto: string[]) => {
+ try {
+ return await db
+ .selectFrom("comuni")
+ .selectAll()
+ .where("catasto", "in", catasto)
+ .execute();
+ } catch (e) {
+ throw new TRPCError({
+ code: "INTERNAL_SERVER_ERROR",
+ message: `Error while getting Comuni: ${(e as Error).message}`,
+ });
+ }
+};
export const getComuneById = async (id: ComuniId) => {
try {
return await db
diff --git a/apps/infoalloggi/src/server/controllers/user.controller.ts b/apps/infoalloggi/src/server/controllers/user.controller.ts
index 1c513e8..07eb28a 100644
--- a/apps/infoalloggi/src/server/controllers/user.controller.ts
+++ b/apps/infoalloggi/src/server/controllers/user.controller.ts
@@ -1,5 +1,5 @@
import { TRPCError } from "@trpc/server";
-import type { Users, UsersId, UsersUpdate } from "~/schemas/public/Users";
+import type { UsersId, UsersUpdate } from "~/schemas/public/Users";
import type { UsersAnagraficaUpdate } from "~/schemas/public/UsersAnagrafica";
import type { Context } from "~/server/api/trpc";
import { db, type Querier } from "~/server/db";
@@ -166,10 +166,11 @@ export const getUserHandler = async ({
}: {
db: Querier;
id: UsersId;
-}): Promise => {
+}) => {
try {
const user = await db
.selectFrom("users")
+ .leftJoin("users_anagrafica", "users_anagrafica.userid", "users.id")
.where("id", "=", id)
.selectAll()
.executeTakeFirst();
@@ -185,7 +186,7 @@ export const getUserHandler = async ({
});
}
};
-export type getUserReturnType = Awaited>;
+export type UserWAnagrafica = Awaited>;
export type NonNullableUser = {
id: NonNullable;
diff --git a/apps/infoalloggi/src/server/services/user.service.ts b/apps/infoalloggi/src/server/services/user.service.ts
index eee0edb..f68a709 100644
--- a/apps/infoalloggi/src/server/services/user.service.ts
+++ b/apps/infoalloggi/src/server/services/user.service.ts
@@ -98,10 +98,8 @@ export const addUser = async ({
}
};
-export type UserWithAnagrafica = Awaited<
- ReturnType
->;
-export const getUserWithAnagraficaHandler = async ({
+export type CompleteUserData = Awaited>;
+export const getClientProfilo = async ({
db,
userId,
}: {