diff --git a/apps/infoalloggi/emails/avviso-interruzione.tsx b/apps/infoalloggi/emails/avviso-interruzione.tsx new file mode 100644 index 0000000..10cd6eb --- /dev/null +++ b/apps/infoalloggi/emails/avviso-interruzione.tsx @@ -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 ( + + <> + + Promemoria durata servizio + +
+ + Gentile Cliente, + + Il periodo per interrompere il servizio (entro 14 giorni dalla + decorrenza) sta per scadere tra pochi giorni. + + + Ricordati che se non sei interessato a confermare nessun + immobile puoi interrompere la ricerca: + + + - Dalla tua area riservata, tramite il pulsante dedicato + + + + - Inviando una mail a:{" "} + + interruzione@infoalloggi.it + + + + Inviando l'interruzione non dovrai pagare il saldo del servizio + acquistato. + + + Restiamo a disposizione, Cordiali saluti + +
+ + + ); +}; + +export default AvvisoInterruzione; diff --git a/apps/infoalloggi/emails/pagamento-conferma.tsx b/apps/infoalloggi/emails/pagamento-conferma.tsx index 4631a52..318eae9 100644 --- a/apps/infoalloggi/emails/pagamento-conferma.tsx +++ b/apps/infoalloggi/emails/pagamento-conferma.tsx @@ -21,8 +21,8 @@ const PagamentoConferma = () => { Puoi controllare lo stato del tuo account nella sezione dedicata. - In allegato troverai una ricevuta di cortesia, la fattura eletnica - arriverà separatamente. + In allegato troverai una ricevuta di cortesia, la fattura + elettronica arriverà separatamente. diff --git a/apps/infoalloggi/src/components/servizio/servizio_actions.tsx b/apps/infoalloggi/src/components/servizio/servizio_actions.tsx index fc59950..739a69c 100644 --- a/apps/infoalloggi/src/components/servizio/servizio_actions.tsx +++ b/apps/infoalloggi/src/components/servizio/servizio_actions.tsx @@ -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 (
+
@@ -116,7 +118,7 @@ const UserActions = () => { if (isAdmin || canUserEdit) { return ( <> - + ); @@ -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 ( + mutate({ servizioId: servizio.servizio_id })} + title="Attivazione Servizio" + > + + + ); +}; + const AddAnnuncio = ({ servizioId, }: { @@ -699,7 +730,6 @@ const EditParametri = () => { ); }; - const EditServizioAdmin = () => { const { servizioId, userId, servizio } = useServizio(); const [open, setOpen] = useState(false); diff --git a/apps/infoalloggi/src/server/api/routers/servizio.ts b/apps/infoalloggi/src/server/api/routers/servizio.ts index de23129..e1e7e6b 100644 --- a/apps/infoalloggi/src/server/api/routers/servizio.ts +++ b/apps/infoalloggi/src/server/api/routers/servizio.ts @@ -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( diff --git a/apps/infoalloggi/src/server/controllers/servizio.controller.ts b/apps/infoalloggi/src/server/controllers/servizio.controller.ts index 34a4fe5..ad7ec9f 100644 --- a/apps/infoalloggi/src/server/controllers/servizio.controller.ts +++ b/apps/infoalloggi/src/server/controllers/servizio.controller.ts @@ -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; diff --git a/apps/infoalloggi/src/server/controllers/stripe.controller.ts b/apps/infoalloggi/src/server/controllers/stripe.controller.ts index 3d2a3b4..c98447e 100644 --- a/apps/infoalloggi/src/server/controllers/stripe.controller.ts +++ b/apps/infoalloggi/src/server/controllers/stripe.controller.ts @@ -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; diff --git a/apps/infoalloggi/src/server/services/mailer.ts b/apps/infoalloggi/src/server/services/mailer.ts index 5a40dd6..4c312f6 100644 --- a/apps/infoalloggi/src/server/services/mailer.ts +++ b/apps/infoalloggi/src/server/services/mailer.ts @@ -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",