feat: implement email sharing functionality for selected listings
This commit is contained in:
parent
07132e8cd6
commit
95315e6605
6 changed files with 177 additions and 20 deletions
|
|
@ -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 (
|
||||
<Base preview="Procedi ora su Infoalloggi.it">
|
||||
<>
|
||||
|
|
@ -48,9 +43,9 @@ const OnboardingServizio = ({
|
|||
<Section className="mb-5">
|
||||
<Button
|
||||
className="mx-auto box-border inline-flex h-10 items-center justify-center whitespace-nowrap rounded-md bg-neutral-100 px-4 py-2 text-center align-middle font-semibold text-neutral-900"
|
||||
href={`${env.BASE_URL}/servizio/pre-onboard/${token}`}
|
||||
href={`${env.BASE_URL}`}
|
||||
>
|
||||
Vai al servizio
|
||||
Vai al Sito
|
||||
</Button>
|
||||
</Section>
|
||||
<Section>
|
||||
|
|
@ -100,4 +95,4 @@ const OnboardingServizio = ({
|
|||
);
|
||||
};
|
||||
|
||||
export default OnboardingServizio;
|
||||
export default ShareAnnunci;
|
||||
|
|
@ -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 = () => {
|
|||
</div>
|
||||
</div>
|
||||
<StripeSection />
|
||||
<SendEmailSection />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
@ -59,3 +68,91 @@ const StripeSection = () => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SendEmailSection = () => {
|
||||
const { data: codici, isLoading: loading } =
|
||||
api.annunci.getOnline_AnnuncioIdCodice.useQuery();
|
||||
const [selectedAnnunci, setSelectedAnnunci] = useState<
|
||||
{ value: AnnunciId; label: string }[]
|
||||
>([]);
|
||||
const [email, setEmail] = useState<string | undefined>(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 (
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<h4 className="font-semibold text-lg">
|
||||
Invia email con annunci selezionati
|
||||
</h4>
|
||||
|
||||
<div className="flex max-w-xl flex-col gap-2">
|
||||
<Label className="font-medium" htmlFor="email-to">
|
||||
Indirizzo email destinatario
|
||||
</Label>
|
||||
<Input
|
||||
id="email-to"
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Inserisci l'indirizzo email"
|
||||
type="email"
|
||||
value={email || ""}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex max-w-xl flex-col gap-2">
|
||||
{loading ? (
|
||||
<LoadingPage />
|
||||
) : (
|
||||
<div className="flex max-w-xl flex-col">
|
||||
<Label className="font-medium" htmlFor="select-annuncio">
|
||||
Seleziona gli annunci da inviare
|
||||
</Label>
|
||||
<MultiSelect
|
||||
defaultValue={undefined}
|
||||
elementId="select-annuncio"
|
||||
elementName="annuncio"
|
||||
isMulti={true}
|
||||
onValueChange={(value) => {
|
||||
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=""
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
aria-label="Cancel Add Annuncio"
|
||||
disabled={!selectedAnnunci.length || !email}
|
||||
onClick={() => {
|
||||
if (!selectedAnnunci.length || !email) return;
|
||||
mutate({
|
||||
annunci: selectedAnnunci.map((annuncio) => annuncio.value),
|
||||
to: email,
|
||||
});
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
Invia
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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}`,
|
||||
);
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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<string[]> => {
|
||||
try {
|
||||
const codici = await db
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue