feat: implement AvvisoInterruzione email template and integrate into mailer service
This commit is contained in:
parent
70cc0e4536
commit
1d1051f25a
7 changed files with 186 additions and 6 deletions
48
apps/infoalloggi/emails/avviso-interruzione.tsx
Normal file
48
apps/infoalloggi/emails/avviso-interruzione.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { Heading, Link, Row, Section, Text } from "@react-email/components";
|
||||||
|
import Base from "./base";
|
||||||
|
export type AvvisoInterruzione_NewMail = {
|
||||||
|
mailType: "avvisoInterruzione";
|
||||||
|
};
|
||||||
|
|
||||||
|
const AvvisoInterruzione = () => {
|
||||||
|
return (
|
||||||
|
<Base noreply preview="Promemoria durata servizio">
|
||||||
|
<>
|
||||||
|
<Heading className="text-center text-2xl leading-8">
|
||||||
|
Promemoria durata servizio
|
||||||
|
</Heading>
|
||||||
|
<Section className="px-5 text-left text-base md:px-12">
|
||||||
|
<Row>
|
||||||
|
<Text>Gentile Cliente,</Text>
|
||||||
|
<Text>
|
||||||
|
Il periodo per interrompere il servizio (entro 14 giorni dalla
|
||||||
|
decorrenza) sta per scadere tra pochi giorni.
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
Ricordati che <b> se non sei interessato a confermare</b> nessun
|
||||||
|
immobile puoi interrompere la ricerca:
|
||||||
|
</Text>
|
||||||
|
<Text className="font-semibold">
|
||||||
|
- Dalla tua area riservata, tramite il pulsante dedicato
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text className="font-semibold">
|
||||||
|
- Inviando una mail a:{" "}
|
||||||
|
<Link href="mailto:interruzione@infoalloggi.it">
|
||||||
|
interruzione@infoalloggi.it
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
Inviando l'interruzione non dovrai pagare il saldo del servizio
|
||||||
|
acquistato.
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text>Restiamo a disposizione, Cordiali saluti</Text>
|
||||||
|
</Row>
|
||||||
|
</Section>
|
||||||
|
</>
|
||||||
|
</Base>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AvvisoInterruzione;
|
||||||
|
|
@ -21,8 +21,8 @@ const PagamentoConferma = () => {
|
||||||
Puoi controllare lo stato del tuo account nella sezione dedicata.
|
Puoi controllare lo stato del tuo account nella sezione dedicata.
|
||||||
</Text>
|
</Text>
|
||||||
<Text>
|
<Text>
|
||||||
In allegato troverai una ricevuta di cortesia, la fattura eletnica
|
In allegato troverai una ricevuta di cortesia, la fattura
|
||||||
arriverà separatamente.
|
elettronica arriverà separatamente.
|
||||||
</Text>
|
</Text>
|
||||||
</Row>
|
</Row>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { add, isBefore } from "date-fns";
|
import { add, isBefore } from "date-fns";
|
||||||
import {
|
import {
|
||||||
Bug,
|
Bug,
|
||||||
|
Check,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ClockAlert,
|
ClockAlert,
|
||||||
Cog,
|
Cog,
|
||||||
|
|
@ -97,6 +98,7 @@ export const AdminServizioActions = () => {
|
||||||
if (!isAdmin) return null;
|
if (!isAdmin) return null;
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
<AdminActivationUfficio />
|
||||||
<AddAnnuncio servizioId={servizio.servizio_id} userId={userId} />
|
<AddAnnuncio servizioId={servizio.servizio_id} userId={userId} />
|
||||||
<EditServizioAdmin />
|
<EditServizioAdmin />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -442,6 +444,35 @@ export const ServizioPacksInfos = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AdminActivationUfficio = () => {
|
||||||
|
const { servizio } = useServizio();
|
||||||
|
const utils = api.useUtils();
|
||||||
|
|
||||||
|
const { mutate } = api.servizio.AdminAttivaServizio_Ufficio.useMutation({
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
},
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success("Servizio attivato con successo");
|
||||||
|
await utils.servizio.invalidate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (servizio.onboardOk) return null;
|
||||||
|
if (!servizio.ordini.some((o) => o.isActive)) return null;
|
||||||
|
return (
|
||||||
|
<Confirm
|
||||||
|
description="Sei sicuro di voler attivare il servizio manualmente?"
|
||||||
|
onConfirm={() => mutate({ servizioId: servizio.servizio_id })}
|
||||||
|
title="Attivazione Servizio"
|
||||||
|
>
|
||||||
|
<Button variant="destructive">
|
||||||
|
<Check />
|
||||||
|
<span>Attivazione Ufficio</span>
|
||||||
|
</Button>
|
||||||
|
</Confirm>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const AddAnnuncio = ({
|
const AddAnnuncio = ({
|
||||||
servizioId,
|
servizioId,
|
||||||
}: {
|
}: {
|
||||||
|
|
@ -699,7 +730,6 @@ const EditParametri = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const EditServizioAdmin = () => {
|
const EditServizioAdmin = () => {
|
||||||
const { servizioId, userId, servizio } = useServizio();
|
const { servizioId, userId, servizio } = useServizio();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import {
|
||||||
} from "~/server/api/trpc";
|
} from "~/server/api/trpc";
|
||||||
import { getPacksPerServizio } from "~/server/controllers/pagamenti.controller";
|
import { getPacksPerServizio } from "~/server/controllers/pagamenti.controller";
|
||||||
import {
|
import {
|
||||||
|
AdminAttivaServizio_Ufficio,
|
||||||
addServizioAnnunci,
|
addServizioAnnunci,
|
||||||
adminUpdateConfermaStatus,
|
adminUpdateConfermaStatus,
|
||||||
attivaServizio_SaltaPagamentoAcconto,
|
attivaServizio_SaltaPagamentoAcconto,
|
||||||
|
|
@ -275,6 +276,15 @@ export const servizioRouter = createTRPCRouter({
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
return await attivaServizio_SaltaPagamentoAcconto(input.ordineId);
|
return await attivaServizio_SaltaPagamentoAcconto(input.ordineId);
|
||||||
}),
|
}),
|
||||||
|
AdminAttivaServizio_Ufficio: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
servizioId: zServizioId,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return await AdminAttivaServizio_Ufficio(input.servizioId);
|
||||||
|
}),
|
||||||
|
|
||||||
removeAnnuncioServizio: protectedProcedure
|
removeAnnuncioServizio: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
|
|
|
||||||
|
|
@ -257,6 +257,22 @@ export const attivaServizio_SaltaPagamentoAcconto = async (
|
||||||
},
|
},
|
||||||
userId: ordine.userId,
|
userId: ordine.userId,
|
||||||
});
|
});
|
||||||
|
await addEventToQueue({
|
||||||
|
data: {
|
||||||
|
tipo: "email",
|
||||||
|
data: {
|
||||||
|
template: {
|
||||||
|
mailType: "avvisoInterruzione",
|
||||||
|
},
|
||||||
|
mail: {
|
||||||
|
subject: "Avviso Interruzione",
|
||||||
|
to: ordine.email,
|
||||||
|
},
|
||||||
|
userId: ordine.userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lock_expires: add(new Date(), { days: 10 }),
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -267,6 +283,57 @@ export const attivaServizio_SaltaPagamentoAcconto = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const AdminAttivaServizio_Ufficio = async (
|
||||||
|
servizioId: ServizioServizioId,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
return await db.transaction().execute(async (tx) => {
|
||||||
|
const data = await tx
|
||||||
|
.selectFrom("servizio")
|
||||||
|
.select("servizio.user_id")
|
||||||
|
.innerJoin("users", "users.id", "servizio.user_id")
|
||||||
|
.select("users.email")
|
||||||
|
.where("servizio_id", "=", servizioId)
|
||||||
|
.executeTakeFirstOrThrow(
|
||||||
|
() => new Error(`Servizio not found - servizioId: ${servizioId}`),
|
||||||
|
);
|
||||||
|
const today = new Date();
|
||||||
|
await tx
|
||||||
|
.updateTable("servizio")
|
||||||
|
.set({
|
||||||
|
decorrenza: today,
|
||||||
|
isOkAcconto: true,
|
||||||
|
onboardOk: true,
|
||||||
|
})
|
||||||
|
.where("servizio_id", "=", servizioId)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await addEventToQueue({
|
||||||
|
data: {
|
||||||
|
tipo: "email",
|
||||||
|
data: {
|
||||||
|
template: {
|
||||||
|
mailType: "avvisoInterruzione",
|
||||||
|
},
|
||||||
|
mail: {
|
||||||
|
subject: "Avviso Interruzione",
|
||||||
|
to: data.email,
|
||||||
|
},
|
||||||
|
userId: data.user_id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lock_expires: add(today, { days: 10 }),
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Error activating servizio from office: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
type DocRef = {
|
type DocRef = {
|
||||||
storageId: string;
|
storageId: string;
|
||||||
ref: UsersStorageUserStorageId | null;
|
ref: UsersStorageUserStorageId | null;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { format } from "date-fns";
|
import { add, format } from "date-fns";
|
||||||
import { env } from "~/env";
|
import { env } from "~/env";
|
||||||
import stripe from "~/lib/stripe";
|
import stripe from "~/lib/stripe";
|
||||||
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
|
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
|
||||||
|
|
@ -154,6 +154,23 @@ export const whIntentSucceededHandler = async ({
|
||||||
},
|
},
|
||||||
lock_expires,
|
lock_expires,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await addEventToQueue({
|
||||||
|
data: {
|
||||||
|
tipo: "email",
|
||||||
|
data: {
|
||||||
|
template: {
|
||||||
|
mailType: "avvisoInterruzione",
|
||||||
|
},
|
||||||
|
mail: {
|
||||||
|
subject: "Avviso Interruzione",
|
||||||
|
to: utente.email,
|
||||||
|
},
|
||||||
|
userId: utente.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lock_expires: add(new Date(), { days: 10 }),
|
||||||
|
});
|
||||||
//SERVIZIO ATTIVATO
|
//SERVIZIO ATTIVATO
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { render } from "@react-email/render";
|
import { render } from "@react-email/render";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
import type { AvvisoInterruzione_NewMail } from "emails/avviso-interruzione";
|
||||||
|
import AvvisoInterruzione from "emails/avviso-interruzione";
|
||||||
import ContattoAdminEmail, {
|
import ContattoAdminEmail, {
|
||||||
type ContattoAdmin_NewMail,
|
type ContattoAdmin_NewMail,
|
||||||
} from "emails/contatto";
|
} from "emails/contatto";
|
||||||
|
|
@ -91,7 +93,8 @@ export type MailsTemplates =
|
||||||
| Generic_NewMail
|
| Generic_NewMail
|
||||||
| SchedaContatto_NewMail
|
| SchedaContatto_NewMail
|
||||||
| Invito_NewMail
|
| Invito_NewMail
|
||||||
| ServizioAttivato_NewMail;
|
| ServizioAttivato_NewMail
|
||||||
|
| AvvisoInterruzione_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;
|
||||||
|
|
@ -179,6 +182,11 @@ export const genMail = async ({ ...mail }: MailsTemplates) => {
|
||||||
mailComponent = ServizioAttivato();
|
mailComponent = ServizioAttivato();
|
||||||
title = "Servizio Attivato";
|
title = "Servizio Attivato";
|
||||||
break;
|
break;
|
||||||
|
case "avvisoInterruzione":
|
||||||
|
//TODO da fare
|
||||||
|
mailComponent = AvvisoInterruzione();
|
||||||
|
title = "Avviso Interruzione";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue