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.
|
||||
</Text>
|
||||
<Text>
|
||||
In allegato troverai una ricevuta di cortesia, la fattura eletnica
|
||||
arriverà separatamente.
|
||||
In allegato troverai una ricevuta di cortesia, la fattura
|
||||
elettronica arriverà separatamente.
|
||||
</Text>
|
||||
</Row>
|
||||
</Section>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { add, isBefore } from "date-fns";
|
||||
import {
|
||||
Bug,
|
||||
Check,
|
||||
ChevronDown,
|
||||
ClockAlert,
|
||||
Cog,
|
||||
|
|
@ -97,6 +98,7 @@ export const AdminServizioActions = () => {
|
|||
if (!isAdmin) return null;
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<AdminActivationUfficio />
|
||||
<AddAnnuncio servizioId={servizio.servizio_id} userId={userId} />
|
||||
<EditServizioAdmin />
|
||||
</div>
|
||||
|
|
@ -116,7 +118,7 @@ const UserActions = () => {
|
|||
if (isAdmin || canUserEdit) {
|
||||
return (
|
||||
<>
|
||||
<EditParametri />
|
||||
<EditParametri />
|
||||
<InterruzioneServizio />
|
||||
</>
|
||||
);
|
||||
|
|
@ -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 = ({
|
||||
servizioId,
|
||||
}: {
|
||||
|
|
@ -699,7 +730,6 @@ const EditParametri = () => {
|
|||
);
|
||||
};
|
||||
|
||||
|
||||
const EditServizioAdmin = () => {
|
||||
const { servizioId, userId, servizio } = useServizio();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
} from "~/server/api/trpc";
|
||||
import { getPacksPerServizio } from "~/server/controllers/pagamenti.controller";
|
||||
import {
|
||||
AdminAttivaServizio_Ufficio,
|
||||
addServizioAnnunci,
|
||||
adminUpdateConfermaStatus,
|
||||
attivaServizio_SaltaPagamentoAcconto,
|
||||
|
|
@ -275,6 +276,15 @@ export const servizioRouter = createTRPCRouter({
|
|||
.mutation(async ({ input }) => {
|
||||
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
|
||||
.input(
|
||||
|
|
|
|||
|
|
@ -257,6 +257,22 @@ export const attivaServizio_SaltaPagamentoAcconto = async (
|
|||
},
|
||||
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;
|
||||
});
|
||||
} 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 = {
|
||||
storageId: string;
|
||||
ref: UsersStorageUserStorageId | null;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { TRPCError } from "@trpc/server";
|
||||
import { format } from "date-fns";
|
||||
import { add, format } from "date-fns";
|
||||
import { env } from "~/env";
|
||||
import stripe from "~/lib/stripe";
|
||||
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
|
||||
|
|
@ -154,6 +154,23 @@ export const whIntentSucceededHandler = async ({
|
|||
},
|
||||
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
|
||||
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { render } from "@react-email/render";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import type { AvvisoInterruzione_NewMail } from "emails/avviso-interruzione";
|
||||
import AvvisoInterruzione from "emails/avviso-interruzione";
|
||||
import ContattoAdminEmail, {
|
||||
type ContattoAdmin_NewMail,
|
||||
} from "emails/contatto";
|
||||
|
|
@ -91,7 +93,8 @@ export type MailsTemplates =
|
|||
| Generic_NewMail
|
||||
| SchedaContatto_NewMail
|
||||
| Invito_NewMail
|
||||
| ServizioAttivato_NewMail;
|
||||
| ServizioAttivato_NewMail
|
||||
| AvvisoInterruzione_NewMail;
|
||||
|
||||
export const genMail = async ({ ...mail }: MailsTemplates) => {
|
||||
let mailComponent: JSX.Element | null = null;
|
||||
|
|
@ -179,6 +182,11 @@ export const genMail = async ({ ...mail }: MailsTemplates) => {
|
|||
mailComponent = ServizioAttivato();
|
||||
title = "Servizio Attivato";
|
||||
break;
|
||||
case "avvisoInterruzione":
|
||||
//TODO da fare
|
||||
mailComponent = AvvisoInterruzione();
|
||||
title = "Avviso Interruzione";
|
||||
break;
|
||||
default:
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue