feat: implement email sending for contact details with PDF attachment
This commit is contained in:
parent
e5c0ca04ee
commit
3ea9354abb
8 changed files with 214 additions and 33 deletions
|
|
@ -41,7 +41,7 @@ const Base = ({
|
||||||
|
|
||||||
<Preview>{preview}</Preview>
|
<Preview>{preview}</Preview>
|
||||||
<Body className="py-2 text-center font-sans text-base text-neutral-700">
|
<Body className="py-2 text-center font-sans text-base text-neutral-700">
|
||||||
<Container className="max-w-xl rounded-lg border border-[#eaeaea] border-solid pt-2 pb-10">
|
<Container className="max-w-2xl rounded-lg border border-[#eaeaea] border-solid pt-2 pb-10">
|
||||||
<Img
|
<Img
|
||||||
alt="Infoalloggi.it"
|
alt="Infoalloggi.it"
|
||||||
className="mx-auto my-2 scale-80"
|
className="mx-auto my-2 scale-80"
|
||||||
|
|
@ -51,9 +51,9 @@ const Base = ({
|
||||||
width={230.5}
|
width={230.5}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Section className="max-w-lg px-8">
|
<Section className="max-w-xl px-4">
|
||||||
<Hr />
|
<Hr />
|
||||||
<Section className="max-w-md">{children}</Section>
|
<Section className="max-w-lg">{children}</Section>
|
||||||
|
|
||||||
<Section className="mt-4">
|
<Section className="mt-4">
|
||||||
{noreply && (
|
{noreply && (
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,33 @@
|
||||||
import { Button, Heading, Row, Section, Text } from "@react-email/components";
|
import { Button, Heading, Row, Section, Text } from "@react-email/components";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
import Base from "./base";
|
import Base from "./base";
|
||||||
export type Generic_NewMail = {
|
export type Generic_NewMail = {
|
||||||
mailType: "generic";
|
mailType: "generic";
|
||||||
props: GenericProps;
|
props: GenericProps;
|
||||||
};
|
};
|
||||||
|
type Corpo = {
|
||||||
|
testo?: undefined;
|
||||||
|
corpo: ReactNode;
|
||||||
|
};
|
||||||
|
type Testo = {
|
||||||
|
testo: string;
|
||||||
|
corpo?: undefined;
|
||||||
|
};
|
||||||
type GenericProps = {
|
type GenericProps = {
|
||||||
title: string;
|
title: string;
|
||||||
noreply?: boolean;
|
noreply?: boolean;
|
||||||
testo: string;
|
|
||||||
link?: {
|
link?: {
|
||||||
href: string;
|
href: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
};
|
};
|
||||||
};
|
} & (Corpo | Testo);
|
||||||
|
|
||||||
const Generic = ({
|
const Generic = ({
|
||||||
title,
|
title,
|
||||||
noreply = false,
|
noreply = false,
|
||||||
testo,
|
testo,
|
||||||
|
corpo,
|
||||||
link = undefined,
|
link = undefined,
|
||||||
}: GenericProps) => {
|
}: GenericProps) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -26,9 +35,13 @@ const Generic = ({
|
||||||
<>
|
<>
|
||||||
<Heading className="text-center text-3xl leading-8">{title}</Heading>
|
<Heading className="text-center text-3xl leading-8">{title}</Heading>
|
||||||
<Section className="px-5 text-left text-base md:px-12">
|
<Section className="px-5 text-left text-base md:px-12">
|
||||||
|
{corpo ? (
|
||||||
|
corpo
|
||||||
|
) : (
|
||||||
<Row>
|
<Row>
|
||||||
<Text>{testo}</Text>
|
<Text>{testo}</Text>
|
||||||
</Row>
|
</Row>
|
||||||
|
)}
|
||||||
{link && (
|
{link && (
|
||||||
<Section className="mb-5">
|
<Section className="mb-5">
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
79
apps/infoalloggi/emails/scheda-annuncio.tsx
Normal file
79
apps/infoalloggi/emails/scheda-annuncio.tsx
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { Heading, Row, Section, Text } from "@react-email/components";
|
||||||
|
import Base from "./base";
|
||||||
|
export type SchedaContatto_NewMail = {
|
||||||
|
mailType: "emailSchedaContatto";
|
||||||
|
props: EmailSchedaContattoProps;
|
||||||
|
};
|
||||||
|
type EmailSchedaContattoProps = {
|
||||||
|
nominativo: string;
|
||||||
|
telefono: string;
|
||||||
|
indirizzo: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EmailSchedaContatto = ({
|
||||||
|
nominativo,
|
||||||
|
telefono,
|
||||||
|
indirizzo,
|
||||||
|
}: EmailSchedaContattoProps) => {
|
||||||
|
return (
|
||||||
|
<Base noreply={true} preview="Dati Contatto Annuncio - Infoalloggi.it">
|
||||||
|
<>
|
||||||
|
<Heading className="text-center text-3xl leading-8">
|
||||||
|
Dati Contatto Annuncio
|
||||||
|
</Heading>
|
||||||
|
<Section className="px-5 text-left md:px-12">
|
||||||
|
<Row>
|
||||||
|
<Text className="text-base">Salve,</Text>
|
||||||
|
|
||||||
|
<Text className="text-base">
|
||||||
|
come da accordi le invio i dettagli del proprietario/referente.
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text className="text-base">
|
||||||
|
Scriva un messaggio WhatsApp ({" "}
|
||||||
|
<a href={`https://wa.me/${telefono}`} rel="noopener noreferrer">
|
||||||
|
Clicca per aprire WhatsApp
|
||||||
|
</a>{" "}
|
||||||
|
) avvisando di aver ricevuto da Infoalloggi il suo contatto e
|
||||||
|
chieda un appuntamento, poi ci aggiorniamo per l'esito.
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text className="text-base">Nominativo: {nominativo}</Text>
|
||||||
|
<Text className="text-base">Tel: {telefono}</Text>
|
||||||
|
<Text className="text-base">Indirizzo immobile: {indirizzo}</Text>
|
||||||
|
|
||||||
|
<Text className="text-base">
|
||||||
|
In allegato trova la scheda completa dell'annuncio
|
||||||
|
</Text>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<Text className="text-base">
|
||||||
|
Per ulteriori informazioni e chiarimenti non esiti a contattarci
|
||||||
|
all'indirizzo{" "}
|
||||||
|
<a href="mailto:web@infoalloggi.it" rel="noopener noreferrer">
|
||||||
|
web@infoalloggi.it
|
||||||
|
</a>{" "}
|
||||||
|
oppure tramite tel. 04241760915.
|
||||||
|
</Text>
|
||||||
|
<Text className="text-base">Cordiali saluti</Text>
|
||||||
|
<br />
|
||||||
|
<Text className="text-xs">
|
||||||
|
{" "}
|
||||||
|
Le informazioni contenute in questo messaggio di posta elettronica
|
||||||
|
sono riservate confidenziali e ne è vietata la diffusione in
|
||||||
|
qualunque modo eseguita. Qualora Lei non fosse la persona a cui il
|
||||||
|
presente messaggio è destinato, La invitiamo gentilmente ad
|
||||||
|
eliminarlo dopo averne dato tempestiva comunicazione al mittente e
|
||||||
|
a non utilizzare in alcun caso il suo contenuto. Qualsiasi
|
||||||
|
utilizzo non autorizzato di questo messaggio e dei suoi eventuali
|
||||||
|
allegati espone il responsabile alle relative conseguenze civili e
|
||||||
|
penali.
|
||||||
|
</Text>
|
||||||
|
</Row>
|
||||||
|
</Section>
|
||||||
|
</>
|
||||||
|
</Base>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EmailSchedaContatto;
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
import html2canvas from "html2canvas-pro";
|
import html2canvas from "html2canvas-pro";
|
||||||
import jsPDF from "jspdf";
|
import jsPDF from "jspdf";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
import { useReactToPrint } from "react-to-print";
|
import { useReactToPrint } from "react-to-print";
|
||||||
|
import z from "zod";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import { Card } from "~/components/ui/card";
|
import { Card } from "~/components/ui/card";
|
||||||
import { filteredCaratteristiche } from "~/hooks/schedaAnnuncioUtils";
|
import { filteredCaratteristiche } from "~/hooks/schedaAnnuncioUtils";
|
||||||
|
|
@ -15,28 +17,63 @@ import type { Annunci } from "~/schemas/public/Annunci";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { IconMatrix } from "./IconComponents";
|
import { IconMatrix } from "./IconComponents";
|
||||||
import { GoogleMapsIcon, LogoSvg, WhatsAppIcon } from "./svgs";
|
import { GoogleMapsIcon, LogoSvg, WhatsAppIcon } from "./svgs";
|
||||||
|
import { Input } from "./ui/input";
|
||||||
|
import { Label } from "./ui/label";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||||
|
|
||||||
export function SchedaAnnuncioStampabile({ data }: { data: Annunci }) {
|
export function SchedaAnnuncioStampabile({ data }: { data: Annunci }) {
|
||||||
|
const [inputEmail, setInputEmail] = useState<string>("");
|
||||||
|
const [openPopover, setOpenPopover] = useState<boolean>(false);
|
||||||
const contentRef = useRef<HTMLDivElement>(null);
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
const reactToPrintFn = useReactToPrint({
|
const reactToPrintFn = useReactToPrint({
|
||||||
contentRef,
|
contentRef,
|
||||||
//print: async (target) => {console.log("Printing...", target.contentDocument);},
|
//print: async (target) => {console.log("Printing...", target.contentDocument);},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutate } = api.comunicazioni.sendSchedaAnnuncioEmail.useMutation();
|
const { mutate } = api.comunicazioni.sendSchedaAnnuncioEmail.useMutation({
|
||||||
const reactToPrintFn2 = useReactToPrint({
|
onSuccess: () => {
|
||||||
|
toast.success("Email inviata con successo!", {
|
||||||
|
id: "send-scheda-annuncio-email",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(`Errore durante l'invio dell'email: ${error.message}`, {
|
||||||
|
id: "send-scheda-annuncio-email",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const sendEmail = useReactToPrint({
|
||||||
contentRef,
|
contentRef,
|
||||||
print: async (target) => {
|
print: async (target) => {
|
||||||
try {
|
try {
|
||||||
const htmlContent = target.contentDocument?.documentElement;
|
const htmlContent = target.contentDocument?.documentElement;
|
||||||
if (!htmlContent) return;
|
if (!htmlContent) {
|
||||||
|
toast.error("Impossibile generare il contenuto per la stampa.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data || !inputEmail || !data.locatore || !data.numero) {
|
||||||
|
toast.error("Dati annuncio o indirizzo email mancanti.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
toast.loading("Invio email in corso...", {
|
||||||
|
id: "send-scheda-annuncio-email",
|
||||||
|
});
|
||||||
|
setOpenPopover(false);
|
||||||
htmlContent.querySelectorAll(".print\\:border-none").forEach((el) => {
|
htmlContent.querySelectorAll(".print\\:border-none").forEach((el) => {
|
||||||
el.classList.remove("border");
|
el.classList.remove("border");
|
||||||
});
|
});
|
||||||
htmlContent.querySelectorAll(".print\\:shadow-none").forEach((el) => {
|
htmlContent.querySelectorAll(".print\\:shadow-none").forEach((el) => {
|
||||||
el.classList.remove("shadow-sm");
|
el.classList.remove("shadow-sm");
|
||||||
});
|
});
|
||||||
|
htmlContent.querySelectorAll("#navigationLink").forEach((el) => {
|
||||||
|
el.classList.remove("flex");
|
||||||
|
el.classList.add("hidden");
|
||||||
|
});
|
||||||
|
htmlContent.querySelectorAll("#mainLogoSvg text").forEach((el) => {
|
||||||
|
el.classList.remove("font-sans");
|
||||||
|
el.classList.add("font-[system-ui]");
|
||||||
|
});
|
||||||
const canvas = await html2canvas(htmlContent, {
|
const canvas = await html2canvas(htmlContent, {
|
||||||
//scale: 2,
|
//scale: 2,
|
||||||
useCORS: true,
|
useCORS: true,
|
||||||
|
|
@ -44,7 +81,7 @@ export function SchedaAnnuncioStampabile({ data }: { data: Annunci }) {
|
||||||
backgroundColor: "#ffffff",
|
backgroundColor: "#ffffff",
|
||||||
});
|
});
|
||||||
|
|
||||||
const imgData = canvas.toDataURL("image/png", 0.85);
|
const imgData = canvas.toDataURL("image/png");
|
||||||
const pdf = new jsPDF("p", "mm", "a4");
|
const pdf = new jsPDF("p", "mm", "a4");
|
||||||
|
|
||||||
// A4 dimensions in mm
|
// A4 dimensions in mm
|
||||||
|
|
@ -119,19 +156,25 @@ export function SchedaAnnuncioStampabile({ data }: { data: Annunci }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const pdfBase64 = pdf.output("dataurlstring");
|
const pdfBase64 = pdf.output("dataurlstring");
|
||||||
console.log(
|
|
||||||
"PDF size:",
|
|
||||||
((pdfBase64.length * 0.75) / 1024 / 1024).toFixed(2),
|
|
||||||
"MB",
|
|
||||||
);
|
|
||||||
|
|
||||||
mutate({
|
mutate({
|
||||||
pdf: pdfBase64,
|
pdf: pdfBase64,
|
||||||
to: "m.pedone98@gmail.com",
|
to: inputEmail,
|
||||||
codice: data.codice,
|
codice: data.codice,
|
||||||
|
numero: data.numero,
|
||||||
|
nominativo: data.locatore,
|
||||||
|
indirizzo: `${data.indirizzo}, ${data.civico} ${data.comune} ${data.cap} (${data.provincia})`,
|
||||||
});
|
});
|
||||||
|
setInputEmail("");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
toast.error(
|
||||||
|
`Errore durante l'invio dell'email: ${(e as Error).message}`,
|
||||||
|
{
|
||||||
|
id: "send-scheda-annuncio-email",
|
||||||
|
},
|
||||||
|
);
|
||||||
console.error("Error during print:", e);
|
console.error("Error during print:", e);
|
||||||
|
setInputEmail("");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -141,13 +184,42 @@ export function SchedaAnnuncioStampabile({ data }: { data: Annunci }) {
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h1 className="font-bold text-2xl">Scheda Annuncio Stampabile</h1>
|
<h1 className="font-bold text-2xl">Scheda Annuncio Stampabile</h1>
|
||||||
|
<div className="flex gap-2">
|
||||||
<Button className="print:hidden" onClick={() => reactToPrintFn()}>
|
<Button className="print:hidden" onClick={() => reactToPrintFn()}>
|
||||||
Stampa
|
Stampa Scheda
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="print:hidden" onClick={() => reactToPrintFn2()}>
|
<Popover onOpenChange={setOpenPopover} open={openPopover}>
|
||||||
Stampa
|
<PopoverTrigger asChild>
|
||||||
|
<Button className="print:hidden" variant="outline">
|
||||||
|
Invia tramite email
|
||||||
</Button>
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="flex w-80 flex-col gap-4 print:hidden">
|
||||||
|
<Label htmlFor="indirizzo">Indirizzo Email</Label>
|
||||||
|
<Input
|
||||||
|
id="indirizzo"
|
||||||
|
onChange={(e) => setInputEmail(e.target.value)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (
|
||||||
|
e.key === "Enter" &&
|
||||||
|
z.safeParse(z.email(), inputEmail).success
|
||||||
|
) {
|
||||||
|
sendEmail();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="email"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
disabled={
|
||||||
|
z.safeParse(z.email(), inputEmail).success === false
|
||||||
|
}
|
||||||
|
onClick={sendEmail}
|
||||||
|
>
|
||||||
|
Invia
|
||||||
|
</Button>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref={contentRef}>
|
<div ref={contentRef}>
|
||||||
<SchedaAnnuncio data={data} />
|
<SchedaAnnuncio data={data} />
|
||||||
|
|
@ -239,6 +311,7 @@ export function SchedaAnnuncio({ data }: { data: Annunci }) {
|
||||||
<a
|
<a
|
||||||
className="flex items-center gap-2 text-blue-600 underline underline-offset-2"
|
className="flex items-center gap-2 text-blue-600 underline underline-offset-2"
|
||||||
href={navigationUrl}
|
href={navigationUrl}
|
||||||
|
id="navigationLink"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -427,6 +427,7 @@ export const LogoSvg = (props: SVGProps<SVGSVGElement>) => (
|
||||||
width={258.758}
|
width={258.758}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
{...props}
|
{...props}
|
||||||
|
id="mainLogoSvg"
|
||||||
>
|
>
|
||||||
<title>{"Infolloggi.it "}</title>
|
<title>{"Infolloggi.it "}</title>
|
||||||
<defs>
|
<defs>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ export type initialValues = {
|
||||||
id: FlagsId;
|
id: FlagsId;
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
|
//TODO salvare flags come boolean
|
||||||
type FormProps = {
|
type FormProps = {
|
||||||
initialValues: initialValues | null;
|
initialValues: initialValues | null;
|
||||||
submitMutation: (values: initialValues) => void;
|
submitMutation: (values: initialValues) => void;
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,9 @@ export const comunicazioniRouter = createTRPCRouter({
|
||||||
z.object({
|
z.object({
|
||||||
pdf: z.string(),
|
pdf: z.string(),
|
||||||
codice: z.string(),
|
codice: z.string(),
|
||||||
|
numero: z.string(),
|
||||||
|
nominativo: z.string(),
|
||||||
|
indirizzo: z.string(),
|
||||||
to: z.email(),
|
to: z.email(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -53,13 +56,14 @@ export const comunicazioniRouter = createTRPCRouter({
|
||||||
const base64Content = input.pdf.includes("base64,")
|
const base64Content = input.pdf.includes("base64,")
|
||||||
? input.pdf.split("base64,")[1]
|
? input.pdf.split("base64,")[1]
|
||||||
: input.pdf;
|
: input.pdf;
|
||||||
|
|
||||||
await NewMail({
|
await NewMail({
|
||||||
template: {
|
template: {
|
||||||
mailType: "generic",
|
mailType: "emailSchedaContatto",
|
||||||
props: {
|
props: {
|
||||||
noreply: true,
|
nominativo: input.nominativo,
|
||||||
testo: `In allegato la scheda annuncio stampabile per l'annuncio con codice ${input.codice}.`,
|
telefono: input.numero,
|
||||||
title: "Scheda Annuncio Stampabile",
|
indirizzo: input.indirizzo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mail: {
|
mail: {
|
||||||
|
|
@ -67,7 +71,7 @@ export const comunicazioniRouter = createTRPCRouter({
|
||||||
to: input.to,
|
to: input.to,
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
filename: `scheda-annuncio-${input.codice}.pdf`,
|
filename: `scheda annuncio ${input.codice}.pdf`,
|
||||||
content: base64Content,
|
content: base64Content,
|
||||||
encoding: "base64",
|
encoding: "base64",
|
||||||
contentType: "application/pdf",
|
contentType: "application/pdf",
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ import type { Recesso_NewMail } from "emails/recesso";
|
||||||
import Recesso from "emails/recesso";
|
import Recesso from "emails/recesso";
|
||||||
import type { RegistrazioneAvvenuta_NewMail } from "emails/registrazione-avvenuta";
|
import type { RegistrazioneAvvenuta_NewMail } from "emails/registrazione-avvenuta";
|
||||||
import RegistrazioneAvvenuta from "emails/registrazione-avvenuta";
|
import RegistrazioneAvvenuta from "emails/registrazione-avvenuta";
|
||||||
|
import EmailSchedaContatto, {
|
||||||
|
type SchedaContatto_NewMail,
|
||||||
|
} from "emails/scheda-annuncio";
|
||||||
import VerificaEmail, {
|
import VerificaEmail, {
|
||||||
type VerificaEmail_NewMail,
|
type VerificaEmail_NewMail,
|
||||||
} from "emails/VerificaEmail";
|
} from "emails/VerificaEmail";
|
||||||
|
|
@ -85,7 +88,8 @@ export type MailsTemplates =
|
||||||
| RegistrazioneAvvenuta_NewMail
|
| RegistrazioneAvvenuta_NewMail
|
||||||
| VerificaEmail_NewMail
|
| VerificaEmail_NewMail
|
||||||
| OnboardingServizio_NewMail
|
| OnboardingServizio_NewMail
|
||||||
| Generic_NewMail;
|
| Generic_NewMail
|
||||||
|
| SchedaContatto_NewMail;
|
||||||
|
|
||||||
export const genMail = async ({ ...mail }: MailsTemplates) => {
|
export const genMail = async ({ ...mail }: MailsTemplates) => {
|
||||||
let mailComponent: JSX.Element | null = null;
|
let mailComponent: JSX.Element | null = null;
|
||||||
|
|
@ -165,6 +169,12 @@ export const genMail = async ({ ...mail }: MailsTemplates) => {
|
||||||
});
|
});
|
||||||
title = mail.props.title || "Email Generica";
|
title = mail.props.title || "Email Generica";
|
||||||
break;
|
break;
|
||||||
|
case "emailSchedaContatto":
|
||||||
|
mailComponent = EmailSchedaContatto({
|
||||||
|
...mail.props,
|
||||||
|
});
|
||||||
|
title = "Scheda Contatto Annuncio";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (mailComponent === null) {
|
if (mailComponent === null) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue