diff --git a/apps/infoalloggi/emails/onboarding-servizio.tsx b/apps/infoalloggi/emails/share-annunci.tsx similarity index 87% rename from apps/infoalloggi/emails/onboarding-servizio.tsx rename to apps/infoalloggi/emails/share-annunci.tsx index 2a4d51f..4e98be6 100644 --- a/apps/infoalloggi/emails/onboarding-servizio.tsx +++ b/apps/infoalloggi/emails/share-annunci.tsx @@ -9,13 +9,12 @@ import { } from "@react-email/components"; import { env } from "~/env"; import Base from "./base"; -export type OnboardingServizio_NewMail = { - mailType: "onboardingServizio"; - props: OnboardingServizioProps; +export type ShareAnnunci_NewMail = { + mailType: "shareAnnunci"; + props: ShareAnnunciProps; }; -type OnboardingServizioProps = { - token: string; +type ShareAnnunciProps = { nome: string; annunci?: { titolo: string | null; @@ -25,11 +24,7 @@ type OnboardingServizioProps = { }[]; }; -const OnboardingServizio = ({ - token, - nome, - annunci, -}: OnboardingServizioProps) => { +const ShareAnnunci = ({ nome, annunci }: ShareAnnunciProps) => { return ( <> @@ -48,9 +43,9 @@ const OnboardingServizio = ({
@@ -100,4 +95,4 @@ const OnboardingServizio = ({ ); }; -export default OnboardingServizio; +export default ShareAnnunci; diff --git a/apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx b/apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx index 3de2e52..0e134ac 100644 --- a/apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx +++ b/apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx @@ -1,6 +1,14 @@ import Head from "next/head"; +import { useState } from "react"; +import toast from "react-hot-toast"; +import Input from "~/components/custom_ui/input"; +import { MultiSelect } from "~/components/custom_ui/multiselect"; import { AreaRiservataLayout } from "~/components/Layout"; +import { LoadingPage } from "~/components/loading"; +import { Button } from "~/components/ui/button"; +import { Label } from "~/components/ui/label"; import type { NextPageWithLayout } from "~/pages/_app"; +import type { AnnunciId } from "~/schemas/public/Annunci"; import { api } from "~/utils/api"; const Impostazioni: NextPageWithLayout = () => { @@ -17,6 +25,7 @@ const Impostazioni: NextPageWithLayout = () => { + ); @@ -59,3 +68,91 @@ const StripeSection = () => { ); }; + +const SendEmailSection = () => { + const { data: codici, isLoading: loading } = + api.annunci.getOnline_AnnuncioIdCodice.useQuery(); + const [selectedAnnunci, setSelectedAnnunci] = useState< + { value: AnnunciId; label: string }[] + >([]); + const [email, setEmail] = useState(undefined); + + const { mutate } = api.comunicazioni.sendAnnunciEmail.useMutation({ + onSuccess: () => { + setSelectedAnnunci([]); + setEmail(""); + toast.success("Email inviata con successo"); + }, + onError: (error) => { + console.error(error); + toast.error(`Errore durante l'invio dell'email: ${error.message}`); + }, + }); + + return ( +
+

+ Invia email con annunci selezionati +

+ +
+ + setEmail(e.target.value)} + placeholder="Inserisci l'indirizzo email" + type="email" + value={email || ""} + /> +
+
+ {loading ? ( + + ) : ( +
+ + { + setSelectedAnnunci( + value.map((v) => ({ + label: v.label, + value: parseInt(v.value) as AnnunciId, + })), + ); + }} + options={ + codici?.map((cod) => ({ + label: cod.codice, + value: cod.id.toString(), + })) || [] + } + placeholder="" + /> +
+ )} +
+ +
+ ); +}; diff --git a/apps/infoalloggi/src/server/api/routers/annunci.ts b/apps/infoalloggi/src/server/api/routers/annunci.ts index 0e70dfc..7d0e3c3 100644 --- a/apps/infoalloggi/src/server/api/routers/annunci.ts +++ b/apps/infoalloggi/src/server/api/routers/annunci.ts @@ -16,6 +16,7 @@ import { getAnnunciMetaByCod, getAnnunciRicerca, getCodici_AnnunciHandler, + getOnline_AnnuncioIdCodice_AnnunciHandler, getOptions_AnnunciHandler, getProprietarioDataHandler, } from "~/server/controllers/annunci.controller"; @@ -39,6 +40,9 @@ export const annunciRouter = createTRPCRouter({ getAnnunciOptions: publicProcedure.query(async () => { return await getOptions_AnnunciHandler(); }), + getOnline_AnnuncioIdCodice: publicProcedure.query(async () => { + return await getOnline_AnnuncioIdCodice_AnnunciHandler(); + }), getAnnuncio: publicProcedure .input( z.object({ diff --git a/apps/infoalloggi/src/server/api/routers/comunicazioni.ts b/apps/infoalloggi/src/server/api/routers/comunicazioni.ts index 066e358..0aeebbe 100644 --- a/apps/infoalloggi/src/server/api/routers/comunicazioni.ts +++ b/apps/infoalloggi/src/server/api/routers/comunicazioni.ts @@ -5,13 +5,14 @@ import { createTRPCRouter, protectedProcedure, } from "~/server/api/trpc"; +import { db } from "~/server/db"; import { deleteAllUserEmails, deleteEmail, getEmails, } from "~/server/services/email.service"; import { NewMail } from "~/server/services/mailer"; -import { zUserId } from "~/server/utils/zod_types"; +import { zAnnuncioId, zUserId } from "~/server/utils/zod_types"; export const comunicazioniRouter = createTRPCRouter({ // genEmail: publicProcedure @@ -95,4 +96,45 @@ export const comunicazioniRouter = createTRPCRouter({ ); } }), + sendAnnunciEmail: adminProcedure + .input( + z.object({ + annunci: zAnnuncioId.array(), + to: z.email(), + }), + ) + .mutation(async ({ input }) => { + try { + const annunci = await db + .selectFrom("annunci") + .select([ + "annunci.titolo_it as titolo", + "annunci.codice", + "annunci.prezzo", + ]) + .innerJoin("images_refs", "images_refs.codice", "annunci.codice") + .select("images_refs.img as immagine") + .where("images_refs.ordine", "=", 0) + .where("annunci.id", "in", input.annunci) + .execute(); + + await NewMail({ + template: { + mailType: "shareAnnunci", + props: { + nome: "Admin", + annunci, + }, + }, + mail: { + subject: `Scheda Annuncio - Infoalloggi.it`, + to: input.to, + }, + }); + } catch (e) { + throw new Error( + `Errore nell'invio dell'email: ${(e as Error).message}`, + ); + } + }), }); diff --git a/apps/infoalloggi/src/server/controllers/annunci.controller.ts b/apps/infoalloggi/src/server/controllers/annunci.controller.ts index 2f94630..2037742 100644 --- a/apps/infoalloggi/src/server/controllers/annunci.controller.ts +++ b/apps/infoalloggi/src/server/controllers/annunci.controller.ts @@ -51,6 +51,27 @@ export const getAnnunciListHandler = async (): Promise< } }; +export const getOnline_AnnuncioIdCodice_AnnunciHandler = async () => { + try { + const data = await db + .selectFrom("annunci") + .select(["codice", "id"]) + .where("annunci.web", "=", true) + .where("stato", "!=", "Sospeso") + .execute(); + if (!data) { + return []; + } + + return data.filter((c) => c.id && c.codice); + } catch (e) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: `Errore query getOnline_AnnuncioIdCodice_AnnunciHandler: ${(e as Error).message}`, + }); + } +}; + export const getCodici_AnnunciHandler = async (): Promise => { try { const codici = await db diff --git a/apps/infoalloggi/src/server/services/mailer.ts b/apps/infoalloggi/src/server/services/mailer.ts index 4ab3371..6a695a4 100644 --- a/apps/infoalloggi/src/server/services/mailer.ts +++ b/apps/infoalloggi/src/server/services/mailer.ts @@ -12,9 +12,6 @@ import type { Generic_NewMail } from "emails/gereric-email"; import Generic from "emails/gereric-email"; import type { Invito_NewMail } from "emails/invito"; import Invito from "emails/invito"; -import OnboardingServizio, { - type OnboardingServizio_NewMail, -} from "emails/onboarding-servizio"; import type { PagamentoConferma_NewMail } from "emails/pagamento-conferma"; import PagamentoConferma from "emails/pagamento-conferma"; import type { PagamentoErrore_NewMail } from "emails/pagamento-errore"; @@ -30,6 +27,7 @@ import RegistrazioneAvvenuta from "emails/registrazione-avvenuta"; import EmailSchedaContatto, { type SchedaContatto_NewMail, } from "emails/scheda-annuncio"; +import ShareAnnunci, { type ShareAnnunci_NewMail } from "emails/share-annunci"; import VerificaEmail, { type VerificaEmail_NewMail, } from "emails/VerificaEmail"; @@ -89,7 +87,7 @@ export type MailsTemplates = | Recesso_NewMail | RegistrazioneAvvenuta_NewMail | VerificaEmail_NewMail - | OnboardingServizio_NewMail + | ShareAnnunci_NewMail | Generic_NewMail | SchedaContatto_NewMail | Invito_NewMail; @@ -160,8 +158,8 @@ export const genMail = async ({ ...mail }: MailsTemplates) => { title = "Verifica email"; break; - case "onboardingServizio": - mailComponent = OnboardingServizio({ + case "shareAnnunci": + mailComponent = ShareAnnunci({ ...mail.props, }); title = "Onboarding Servizio";