feat: enhance email management features and improve flag handling
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
parent
06c93024f7
commit
e33ee7a8cd
18 changed files with 451 additions and 142 deletions
|
|
@ -1,15 +1,266 @@
|
||||||
|
import { ExternalLink, Maximize2, Trash2 } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
import { type IframeHTMLAttributes, useState } from "react";
|
import { type IframeHTMLAttributes, useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { Confirm } from "~/components/confirm";
|
||||||
|
import { LoadingPage } from "~/components/loading";
|
||||||
import {
|
import {
|
||||||
Accordion,
|
Accordion,
|
||||||
AccordionContent,
|
AccordionContent,
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
} from "~/components/ui/accordion";
|
} from "~/components/ui/accordion";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "~/components/ui/dialog";
|
||||||
|
import { Label } from "~/components/ui/label";
|
||||||
|
import { Switch } from "~/components/ui/switch";
|
||||||
|
import { FormEditQueueMail } from "~/forms/FormEditQueueMail";
|
||||||
import { useMediaQuery } from "~/hooks/use-media-query";
|
import { useMediaQuery } from "~/hooks/use-media-query";
|
||||||
import type { UsersId } from "~/schemas/public/Users";
|
import type { UsersId } from "~/schemas/public/Users";
|
||||||
|
import type { Email_EventData } from "~/server/controllers/event_queue.controller";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
|
||||||
|
export const AdminComunicazioni = () => {
|
||||||
|
const { data: emails, isLoading: isEmailsLoading } =
|
||||||
|
api.comunicazioni.getEmails.useQuery();
|
||||||
|
const { data: queue, isLoading: isQueueLoading } =
|
||||||
|
api.comunicazioni.getQueuedEmails.useQuery();
|
||||||
|
const { data: flag, isLoading: isFlagLoading } =
|
||||||
|
api.flags.GetFlagValue.useQuery("EMAIL_ENABLED", {
|
||||||
|
refetchOnWindowFocus: true,
|
||||||
|
});
|
||||||
|
const utils = api.useUtils();
|
||||||
|
const { mutate: deleteEmail } = api.comunicazioni.deleteEmail.useMutation({
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success("Email eliminata con successo");
|
||||||
|
setOpenEmail("unset");
|
||||||
|
await utils.comunicazioni.getEmails.invalidate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { mutate: clearEmailQueue } =
|
||||||
|
api.comunicazioni.clearEmailQueue.useMutation({
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success("Coda email pulita");
|
||||||
|
await utils.comunicazioni.getQueuedEmails.invalidate();
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error(error);
|
||||||
|
toast.error("Errore durante la pulizia della coda email");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { mutate: removeFromQueue } =
|
||||||
|
api.comunicazioni.removeFromEmailQueue.useMutation({
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success("Email rimossa dalla coda");
|
||||||
|
await utils.comunicazioni.getQueuedEmails.invalidate();
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error(error);
|
||||||
|
toast.error("Errore durante la rimozione dell'email dalla coda");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { mutate: updateEvent } = api.comunicazioni.updateEvent.useMutation({
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success("Email aggiornata nella coda");
|
||||||
|
await utils.comunicazioni.getQueuedEmails.invalidate();
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error(error);
|
||||||
|
toast.error("Errore durante l'aggiornamento dell'email nella coda");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { mutate: editFlag } = api.flags.EditFlag.useMutation({
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(error.message);
|
||||||
|
},
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success("Successo");
|
||||||
|
await utils.flags.GetFlagValue.invalidate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [openEmail, setOpenEmail] = useState<string | "unset">("unset");
|
||||||
|
|
||||||
|
const mobile = useMediaQuery("(max-width: 768px)");
|
||||||
|
const iframeHeight = mobile ? 600 : 500;
|
||||||
|
if (isEmailsLoading || isQueueLoading || isFlagLoading) {
|
||||||
|
return <LoadingPage />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto flex w-full flex-col gap-4 py-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Label htmlFor="comms">Abilita comunicazioni Globale</Label>
|
||||||
|
<Switch
|
||||||
|
className="data-[state=checked]:bg-neutral-700"
|
||||||
|
defaultChecked={flag ?? false}
|
||||||
|
id="comms"
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
editFlag({ id: "EMAIL_ENABLED", value: checked });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div className="flex w-full flex-col gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<h3>Email in coda: {queue?.length || 0}</h3>{" "}
|
||||||
|
<Confirm
|
||||||
|
description="Sei sicuro di voler svuotare la coda email?"
|
||||||
|
onConfirm={() => {
|
||||||
|
clearEmailQueue();
|
||||||
|
}}
|
||||||
|
title="Elimina"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
aria-label="Delete"
|
||||||
|
disabled={queue?.length === 0}
|
||||||
|
size="sm"
|
||||||
|
variant="destructive"
|
||||||
|
>
|
||||||
|
<Trash2 />
|
||||||
|
Pulisci coda email
|
||||||
|
</Button>
|
||||||
|
</Confirm>
|
||||||
|
</div>
|
||||||
|
{queue?.length === 0 && <div>Nessuna email in coda</div>}
|
||||||
|
{queue?.map((email) => {
|
||||||
|
const mailData = email.data as Email_EventData;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-2 rounded-md border p-1.5"
|
||||||
|
key={email.event_id}
|
||||||
|
>
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button size="sm" variant="outline">
|
||||||
|
<Maximize2 />
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-xl">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Dettagli</DialogTitle>
|
||||||
|
<DialogDescription className="sr-only">
|
||||||
|
dettagli dell'email in coda
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<FormEditQueueMail
|
||||||
|
data={email}
|
||||||
|
del={() => removeFromQueue({ eventId: email.event_id })}
|
||||||
|
submitMutation={(values) => {
|
||||||
|
updateEvent({ eventId: email.event_id, data: values });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
<span className="font-semibold">{email.username}</span>
|
||||||
|
<span>
|
||||||
|
{mailData.data.mail.subject ||
|
||||||
|
mailData.data.template?.mailType ||
|
||||||
|
"Nessun oggetto"}
|
||||||
|
</span>
|
||||||
|
<span className="text-muted-foreground text-sm">
|
||||||
|
data invio:{" "}
|
||||||
|
{new Date(email.lock_expires).toLocaleDateString("it-IT", {
|
||||||
|
day: "2-digit",
|
||||||
|
month: "2-digit",
|
||||||
|
year: "numeric",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div className="flex w-full flex-col gap-2">
|
||||||
|
<h3>Email inviate:</h3>
|
||||||
|
{emails?.length === 0 && <div>Nessuna email inviata</div>}
|
||||||
|
<Accordion
|
||||||
|
className="space-y-4"
|
||||||
|
collapsible
|
||||||
|
onValueChange={setOpenEmail}
|
||||||
|
type="single"
|
||||||
|
value={openEmail}
|
||||||
|
>
|
||||||
|
{emails?.map((email) => {
|
||||||
|
const isOpen = openEmail === email.id_email.toString();
|
||||||
|
return (
|
||||||
|
<AccordionItem
|
||||||
|
className="space-y-2 border-none"
|
||||||
|
key={email.id_email}
|
||||||
|
value={email.id_email.toString()}
|
||||||
|
>
|
||||||
|
<AccordionTrigger className="rounded-md bg-muted px-4 py-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>{email.user}</span>
|
||||||
|
<span className="text-muted-foreground text-sm">
|
||||||
|
“{email.title}”
|
||||||
|
</span>
|
||||||
|
-
|
||||||
|
<span>
|
||||||
|
{new Date(email.created_at).toLocaleDateString("it-IT", {
|
||||||
|
day: "2-digit",
|
||||||
|
month: "2-digit",
|
||||||
|
year: "numeric",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent className="overflow-clip rounded-md border">
|
||||||
|
<div className="flex h-7 w-full items-center justify-end gap-4 bg-secondary px-4">
|
||||||
|
<Link
|
||||||
|
href={`/area-riservata/admin/user-view/comunicazioni/${email.userId}`}
|
||||||
|
>
|
||||||
|
<Button className="h-5">
|
||||||
|
<span>Vai alle email dell'utente</span>
|
||||||
|
<ExternalLink className="size-3" />
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Button
|
||||||
|
className="h-5"
|
||||||
|
onClick={() =>
|
||||||
|
confirm(
|
||||||
|
"Sei sicuro di voler eliminare questa email?",
|
||||||
|
) &&
|
||||||
|
deleteEmail({
|
||||||
|
id_email: email.id_email,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
variant="destructive"
|
||||||
|
>
|
||||||
|
Rimuovi
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isOpen && (
|
||||||
|
<LoadingEmailIframe
|
||||||
|
height={iframeHeight}
|
||||||
|
srcDoc={email.html}
|
||||||
|
title="Email Content"
|
||||||
|
width="100%"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const EmailAccordion = ({
|
export const EmailAccordion = ({
|
||||||
userId,
|
userId,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
|
|
@ -17,7 +268,7 @@ export const EmailAccordion = ({
|
||||||
userId: UsersId;
|
userId: UsersId;
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { data } = api.comunicazioni.getEmails.useQuery({
|
const { data } = api.comunicazioni.getUserEmails.useQuery({
|
||||||
userId,
|
userId,
|
||||||
});
|
});
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
|
|
@ -25,7 +276,7 @@ export const EmailAccordion = ({
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
toast.success("Email eliminata con successo");
|
toast.success("Email eliminata con successo");
|
||||||
setOpenEmail("unset");
|
setOpenEmail("unset");
|
||||||
await utils.comunicazioni.getEmails.invalidate({ userId });
|
await utils.comunicazioni.getUserEmails.invalidate({ userId });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ export const UserViewHeader = () => {
|
||||||
},
|
},
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
toast.success("Email inviata con successo");
|
toast.success("Email inviata con successo");
|
||||||
await utils.comunicazioni.getEmails.invalidate({
|
await utils.comunicazioni.getUserEmails.invalidate({
|
||||||
userId: data?.id || "",
|
userId: data?.id || "",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -327,7 +327,7 @@ const PostConfermaSection = () => {
|
||||||
<div className="flex flex-col gap-1 rounded-md border border-card bg-card p-4">
|
<div className="flex flex-col gap-1 rounded-md border border-card bg-card p-4">
|
||||||
<h3 className="font-semibold text-green-500">
|
<h3 className="font-semibold text-green-500">
|
||||||
{locale === "it"
|
{locale === "it"
|
||||||
? "Ci siamo quasi! Stiamo preparando il contratto per te."
|
? "Immobile Confermato! Stiamo preparando il contratto per te."
|
||||||
: "Almost there! We are preparing the contract for you."}
|
: "Almost there! We are preparing the contract for you."}
|
||||||
</h3>
|
</h3>
|
||||||
<h3 className="font-semibold">
|
<h3 className="font-semibold">
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,13 @@ import {
|
||||||
} from "../ui/collapsible";
|
} from "../ui/collapsible";
|
||||||
import { AnnuncioDettaglio, TipoBadge } from "./annuncio_dettaglio";
|
import { AnnuncioDettaglio, TipoBadge } from "./annuncio_dettaglio";
|
||||||
|
|
||||||
export const AnnuncioCard = ({ className }: { className?: string }) => {
|
export const AnnuncioCard = ({
|
||||||
|
className,
|
||||||
|
userInteractionDisabled = false,
|
||||||
|
}: {
|
||||||
|
className?: string;
|
||||||
|
userInteractionDisabled?: boolean;
|
||||||
|
}) => {
|
||||||
const { isAdmin } = useServizio();
|
const { isAdmin } = useServizio();
|
||||||
const { data } = useServizioAnnuncio();
|
const { data } = useServizioAnnuncio();
|
||||||
|
|
||||||
|
|
@ -67,6 +73,15 @@ export const AnnuncioCard = ({ className }: { className?: string }) => {
|
||||||
|
|
||||||
<AdminActions />
|
<AdminActions />
|
||||||
</div>
|
</div>
|
||||||
|
) : userInteractionDisabled ? (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Contatti:{" "}
|
||||||
|
{data.open_contatti_at
|
||||||
|
? `Ottenuti il ${format(data.open_contatti_at, "dd/MM/yyyy HH:mm")}`
|
||||||
|
: "Non ancora ottenuti"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<UserActions />
|
<UserActions />
|
||||||
)
|
)
|
||||||
|
|
@ -149,10 +164,10 @@ export const AnnuncioCardRichieste = ({
|
||||||
<div className="flex w-full flex-col gap-4">
|
<div className="flex w-full flex-col gap-4">
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
Visto contatti:{" "}
|
Contatti:{" "}
|
||||||
{data.open_contatti_at
|
{data.open_contatti_at
|
||||||
? format(data.open_contatti_at, "dd/MM/yyyy HH:mm")
|
? `Ottenuti il ${format(data.open_contatti_at, "dd/MM/yyyy HH:mm")}`
|
||||||
: "Non ancora visto"}
|
: "Non ancora ottenuti"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -164,7 +179,7 @@ export const AnnuncioCardRichieste = ({
|
||||||
</Link>
|
</Link>
|
||||||
<IncrociDialog
|
<IncrociDialog
|
||||||
annuncioId={data.id}
|
annuncioId={data.id}
|
||||||
props={{ className: "w-fit" , size: "sm"}}
|
props={{ className: "w-fit", size: "sm" }}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<AnnuncioContatti data={data} />
|
<AnnuncioContatti data={data} />
|
||||||
|
|
|
||||||
|
|
@ -568,8 +568,11 @@ const Main = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const InConferma = servizio.annunci.filter((a) => a.status_conferma !== null);
|
const InConferma = servizio.annunci.filter((a) => a.status_conferma !== null);
|
||||||
|
const NonInConferma = servizio.annunci.filter(
|
||||||
|
(a) => a.status_conferma === null,
|
||||||
|
);
|
||||||
|
|
||||||
// in conferma, mostra solo annunci confermati
|
// in conferma
|
||||||
if (servizio.isOkSaldo && InConferma.length > 0) {
|
if (servizio.isOkSaldo && InConferma.length > 0) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -584,20 +587,18 @@ const Main = () => {
|
||||||
</ServizioAnnuncioProvider>
|
</ServizioAnnuncioProvider>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{isAdmin && (
|
|
||||||
<div className="w-full space-y-2">
|
<div className="w-full space-y-2">
|
||||||
<h4 className="text-lg">Altri annunci:</h4>
|
<h4 className="text-lg">Altri annunci:</h4>
|
||||||
<div className="@container grid grid-flow-row @sm:grid-cols-2 grid-cols-1 gap-4">
|
<div className="@container grid grid-flow-row @sm:grid-cols-2 grid-cols-1 gap-4">
|
||||||
{servizio.annunci
|
{NonInConferma.map((data) => {
|
||||||
.filter((a) => a.status_conferma === null)
|
|
||||||
.map((data) => {
|
|
||||||
return (
|
return (
|
||||||
<ServizioAnnuncioProvider
|
<ServizioAnnuncioProvider
|
||||||
data={data}
|
data={data}
|
||||||
key={data.id}
|
key={data.id}
|
||||||
servizioId={servizio.servizio_id}
|
servizioId={servizio.servizio_id}
|
||||||
>
|
>
|
||||||
<AnnuncioCard />
|
<AnnuncioCard userInteractionDisabled />
|
||||||
</ServizioAnnuncioProvider>
|
</ServizioAnnuncioProvider>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
@ -607,7 +608,6 @@ const Main = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -653,7 +653,7 @@ const Main = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="@container grid grid-flow-row @sm:grid-cols-2 grid-cols-1 gap-4">
|
<div className="@container grid grid-flow-row @sm:grid-cols-2 grid-cols-1 gap-4">
|
||||||
{servizio.annunci.map((data) => {
|
{NonInConferma.map((data) => {
|
||||||
return (
|
return (
|
||||||
<ServizioAnnuncioProvider
|
<ServizioAnnuncioProvider
|
||||||
data={data}
|
data={data}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,13 @@ export const DocInfo = ({
|
||||||
const { data: file_info } = api.storage.getFileInfos.useQuery({
|
const { data: file_info } = api.storage.getFileInfos.useQuery({
|
||||||
storageId,
|
storageId,
|
||||||
});
|
});
|
||||||
if (!file_info) return null;
|
if (!file_info)
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<p>File non trovato</p>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full flex-col gap-2">
|
<div className="flex w-full flex-col gap-2">
|
||||||
<Label htmlFor="selected-file">File Selezionato: </Label>
|
<Label htmlFor="selected-file">File Selezionato: </Label>
|
||||||
|
|
|
||||||
|
|
@ -62,9 +62,7 @@ import { cn, redirectTo500 } from "~/lib/utils";
|
||||||
import { AnnuncioContext, useAnnuncio } from "~/providers/AnnuncioProvider";
|
import { AnnuncioContext, useAnnuncio } from "~/providers/AnnuncioProvider";
|
||||||
import { useTranslation } from "~/providers/I18nProvider";
|
import { useTranslation } from "~/providers/I18nProvider";
|
||||||
import { buildRicercaUrl } from "~/providers/RicercaProvider";
|
import { buildRicercaUrl } from "~/providers/RicercaProvider";
|
||||||
import {
|
import { tipologiaPosizioneEnum } from "~/schemas/public/TipologiaPosizioneEnum";
|
||||||
tipologiaPosizioneEnum,
|
|
||||||
} from "~/schemas/public/TipologiaPosizioneEnum";
|
|
||||||
import type { AnnuncioData } from "~/server/controllers/annunci.controller";
|
import type { AnnuncioData } from "~/server/controllers/annunci.controller";
|
||||||
import { generateSSGHelper } from "~/server/utils/ssgHelper";
|
import { generateSSGHelper } from "~/server/utils/ssgHelper";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
|
|
@ -201,9 +199,9 @@ export async function getStaticProps(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const flag = await ssg.flags.GetFlagValue.fetch({
|
const flag = await ssg.flags.GetFlagValue.fetch(
|
||||||
id: "ANNUNCIO_INTERACTIONS_DISABLED",
|
"ANNUNCIO_INTERACTIONS_DISABLED",
|
||||||
});
|
);
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
data: result.value,
|
data: result.value,
|
||||||
|
|
|
||||||
|
|
@ -167,11 +167,7 @@ const EditModal = ({
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<FormFlags
|
<FormFlags
|
||||||
handleRemove={(id) =>
|
handleRemove={(id) => remove(id)}
|
||||||
remove({
|
|
||||||
id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
initialValues={initial}
|
initialValues={initial}
|
||||||
submitMutation={(fields) => {
|
submitMutation={(fields) => {
|
||||||
if (initial) {
|
if (initial) {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import { FormEditQueueMail } from "~/forms/FormEditQueueMail";
|
||||||
import { FormAddCodaEmail } from "~/forms/FormNewMail";
|
import { FormAddCodaEmail } from "~/forms/FormNewMail";
|
||||||
import type { NextPageWithLayout } from "~/pages/_app";
|
import type { NextPageWithLayout } from "~/pages/_app";
|
||||||
import { useEnforcedSession } from "~/providers/SessionProvider";
|
import { useEnforcedSession } from "~/providers/SessionProvider";
|
||||||
import { usersId, type UsersId } from "~/schemas/public/Users";
|
import { type UsersId, usersId } from "~/schemas/public/Users";
|
||||||
import type { Email_EventData } from "~/server/controllers/event_queue.controller";
|
import type { Email_EventData } from "~/server/controllers/event_queue.controller";
|
||||||
|
|
||||||
import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper";
|
import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper";
|
||||||
|
|
@ -50,7 +50,7 @@ const ComunicazioniUser: NextPageWithLayout<ComunicazioniUserProps> = ({
|
||||||
},
|
},
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
toast.success("Comunicazioni eliminate");
|
toast.success("Comunicazioni eliminate");
|
||||||
await utils.comunicazioni.getEmails.invalidate({ userId });
|
await utils.comunicazioni.getUserEmails.invalidate({ userId });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -116,7 +116,7 @@ export const getServerSideProps = (async (context) => {
|
||||||
|
|
||||||
const helpers = await TrpcAuthedFetchingIstance({ access_token });
|
const helpers = await TrpcAuthedFetchingIstance({ access_token });
|
||||||
if (helpers) {
|
if (helpers) {
|
||||||
await helpers.trpc.comunicazioni.getEmails.prefetch({
|
await helpers.trpc.comunicazioni.getUserEmails.prefetch({
|
||||||
userId: parsed.data,
|
userId: parsed.data,
|
||||||
});
|
});
|
||||||
await helpers.trpc.comunicazioni.getUserQueuedEmails.prefetch({
|
await helpers.trpc.comunicazioni.getUserQueuedEmails.prefetch({
|
||||||
|
|
@ -187,7 +187,7 @@ const Coda = ({ userId }: { userId: UsersId }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { mutate: removeFromQueue } =
|
const { mutate: removeFromQueue } =
|
||||||
api.comunicazioni.removeFromUserEmailQueue.useMutation({
|
api.comunicazioni.removeFromEmailQueue.useMutation({
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
toast.success("Email rimossa dalla coda");
|
toast.success("Email rimossa dalla coda");
|
||||||
await utils.comunicazioni.getUserQueuedEmails.invalidate({ userId });
|
await utils.comunicazioni.getUserQueuedEmails.invalidate({ userId });
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { EmailAccordion } from "~/components/area-riservata/comunicazioni";
|
import {
|
||||||
|
AdminComunicazioni,
|
||||||
|
EmailAccordion,
|
||||||
|
} from "~/components/area-riservata/comunicazioni";
|
||||||
import { AreaRiservataLayout } from "~/components/Layout";
|
import { AreaRiservataLayout } from "~/components/Layout";
|
||||||
import type { NextPageWithLayout } from "~/pages/_app";
|
import type { NextPageWithLayout } from "~/pages/_app";
|
||||||
import { useEnforcedSession } from "~/providers/SessionProvider";
|
import { useEnforcedSession } from "~/providers/SessionProvider";
|
||||||
|
|
@ -9,7 +12,7 @@ import { useEnforcedSession } from "~/providers/SessionProvider";
|
||||||
* Mostra le comunicazioni email inviate all'utente.
|
* Mostra le comunicazioni email inviate all'utente.
|
||||||
*/
|
*/
|
||||||
const Comunicazioni: NextPageWithLayout = () => {
|
const Comunicazioni: NextPageWithLayout = () => {
|
||||||
const session = useEnforcedSession();
|
const { user } = useEnforcedSession();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -21,11 +24,11 @@ const Comunicazioni: NextPageWithLayout = () => {
|
||||||
<div className="p-1">
|
<div className="p-1">
|
||||||
<h1 className="font-bold text-2xl">Comunicazioni</h1>
|
<h1 className="font-bold text-2xl">Comunicazioni</h1>
|
||||||
</div>
|
</div>
|
||||||
|
{user.isAdmin ? (
|
||||||
<EmailAccordion
|
<AdminComunicazioni />
|
||||||
isAdmin={session.user.isAdmin}
|
) : (
|
||||||
userId={session.user.id}
|
<EmailAccordion isAdmin={user.isAdmin} userId={user.id} />
|
||||||
/>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -120,9 +120,7 @@ export const getServerSideProps = (async (context) => {
|
||||||
return redirectTo500;
|
return redirectTo500;
|
||||||
}
|
}
|
||||||
|
|
||||||
const flag = await helper.trpc.flags.GetFlagValue.fetch({
|
const flag = await helper.trpc.flags.GetFlagValue.fetch("STRIPE_DISABLED");
|
||||||
id: "STRIPE_DISABLED",
|
|
||||||
});
|
|
||||||
const payment_type = context.query?.type;
|
const payment_type = context.query?.type;
|
||||||
if (typeof payment_type !== "string") {
|
if (typeof payment_type !== "string") {
|
||||||
console.error("Error: payment type is not a string");
|
console.error("Error: payment type is not a string");
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@ import {
|
||||||
} from "~/server/api/trpc";
|
} from "~/server/api/trpc";
|
||||||
import {
|
import {
|
||||||
addEventToQueue,
|
addEventToQueue,
|
||||||
|
clearEmailQueue,
|
||||||
clearUserEmailQueue,
|
clearUserEmailQueue,
|
||||||
|
getEmailQueue,
|
||||||
removeEventFromQueue,
|
removeEventFromQueue,
|
||||||
updateEvent,
|
updateEvent,
|
||||||
userEmailQueue,
|
userEmailQueue,
|
||||||
|
|
@ -28,16 +30,20 @@ import {
|
||||||
deleteAllUserEmails,
|
deleteAllUserEmails,
|
||||||
deleteEmail,
|
deleteEmail,
|
||||||
getEmails,
|
getEmails,
|
||||||
|
getUserEmails,
|
||||||
} from "~/server/services/email.service";
|
} from "~/server/services/email.service";
|
||||||
import type { MailsTemplates } from "~/server/services/mail-templates";
|
import type { MailsTemplates } from "~/server/services/mail-templates";
|
||||||
import { NewMail } from "~/server/services/mailer";
|
import { NewMail } from "~/server/services/mailer";
|
||||||
import { CONDIZIONI_DEFAULT } from "~/server/services/prezziario.service";
|
import { CONDIZIONI_DEFAULT } from "~/server/services/prezziario.service";
|
||||||
|
|
||||||
export const comunicazioniRouter = createTRPCRouter({
|
export const comunicazioniRouter = createTRPCRouter({
|
||||||
getEmails: protectedProcedure
|
getEmails: adminProcedure.query(async () => {
|
||||||
|
return await getEmails();
|
||||||
|
}),
|
||||||
|
getUserEmails: protectedProcedure
|
||||||
.input(z.object({ userId: usersId }))
|
.input(z.object({ userId: usersId }))
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
return await getEmails({
|
return await getUserEmails({
|
||||||
userId: input.userId,
|
userId: input.userId,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
@ -220,6 +226,9 @@ export const comunicazioniRouter = createTRPCRouter({
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
return await userEmailQueue(input.userId);
|
return await userEmailQueue(input.userId);
|
||||||
}),
|
}),
|
||||||
|
getQueuedEmails: adminProcedure.query(async () => {
|
||||||
|
return await getEmailQueue();
|
||||||
|
}),
|
||||||
addToUserEmailQueue: adminProcedure
|
addToUserEmailQueue: adminProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
|
|
@ -264,7 +273,7 @@ export const comunicazioniRouter = createTRPCRouter({
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
removeFromUserEmailQueue: adminProcedure
|
removeFromEmailQueue: adminProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
eventId: eventQueueEventId,
|
eventId: eventQueueEventId,
|
||||||
|
|
@ -273,7 +282,9 @@ export const comunicazioniRouter = createTRPCRouter({
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
return await removeEventFromQueue(input.eventId);
|
return await removeEventFromQueue(input.eventId);
|
||||||
}),
|
}),
|
||||||
|
clearEmailQueue: adminProcedure.mutation(async () => {
|
||||||
|
return await clearEmailQueue();
|
||||||
|
}),
|
||||||
clearUserEmailQueue: adminProcedure
|
clearUserEmailQueue: adminProcedure
|
||||||
.input(z.object({ userId: usersId }))
|
.input(z.object({ userId: usersId }))
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
import { flagsId } from "~/schemas/public/Flags";
|
import { FlagsSchema, flagsId } from "~/schemas/public/Flags";
|
||||||
import {
|
import {
|
||||||
adminProcedure,
|
adminProcedure,
|
||||||
createTRPCRouter,
|
createTRPCRouter,
|
||||||
|
|
@ -12,47 +12,26 @@ import {
|
||||||
GetFlagValueHandler,
|
GetFlagValueHandler,
|
||||||
RemoveFlagHandler,
|
RemoveFlagHandler,
|
||||||
} from "~/server/controllers/flags.controller";
|
} from "~/server/controllers/flags.controller";
|
||||||
import { db } from "~/server/db";
|
|
||||||
|
|
||||||
export const flagsRouter = createTRPCRouter({
|
export const flagsRouter = createTRPCRouter({
|
||||||
AddFlag: adminProcedure
|
AddFlag: adminProcedure.input(FlagsSchema).mutation(async ({ input }) => {
|
||||||
.input(
|
return await AddFlagHandler(input);
|
||||||
z.object({
|
|
||||||
id: flagsId,
|
|
||||||
value: z.boolean(),
|
|
||||||
}),
|
}),
|
||||||
)
|
EditFlag: adminProcedure.input(FlagsSchema).mutation(async ({ input }) => {
|
||||||
.mutation(async ({ input }) => {
|
return await EditFlagHandler(input);
|
||||||
return await AddFlagHandler({ db, input });
|
|
||||||
}),
|
|
||||||
EditFlag: adminProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
id: flagsId,
|
|
||||||
value: z.boolean(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await EditFlagHandler({ db, input });
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
GetFlags: adminProcedure.query(async () => {
|
GetFlags: adminProcedure.query(async () => {
|
||||||
return await GetFlagHandler({ db });
|
return await GetFlagHandler();
|
||||||
}),
|
}),
|
||||||
|
|
||||||
GetFlagValue: publicProcedure
|
GetFlagValue: publicProcedure
|
||||||
.input(z.object({ id: z.string() }))
|
.input(z.string())
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
return await GetFlagValueHandler({ db, id: input.id });
|
return await GetFlagValueHandler(input);
|
||||||
}),
|
}),
|
||||||
RemoveFlag: adminProcedure
|
RemoveFlag: adminProcedure.input(flagsId).mutation(async ({ input }) => {
|
||||||
|
return await RemoveFlagHandler(input);
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
id: flagsId,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await RemoveFlagHandler({ db, id: input.id });
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -223,7 +223,32 @@ export const processEvents = async () => {
|
||||||
}
|
}
|
||||||
return { message: `${events.length} events processed` };
|
return { message: `${events.length} events processed` };
|
||||||
};
|
};
|
||||||
|
export const getEmailQueue = async () => {
|
||||||
|
try {
|
||||||
|
const events = await db
|
||||||
|
.selectFrom("event_queue")
|
||||||
|
.selectAll("event_queue")
|
||||||
|
.select((eb) => [
|
||||||
|
eb
|
||||||
|
.selectFrom("users")
|
||||||
|
.select("username")
|
||||||
|
.whereRef(
|
||||||
|
"users.id",
|
||||||
|
"=",
|
||||||
|
sql`(event_queue.data->'data'->>'userId')::uuid`,
|
||||||
|
)
|
||||||
|
.as("username"),
|
||||||
|
])
|
||||||
|
.where(sql`data->>'tipo'`, "=", "email")
|
||||||
|
.execute();
|
||||||
|
return events;
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Error while getting email events from queue, ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
export const userEmailQueue = async (userId: UsersId) => {
|
export const userEmailQueue = async (userId: UsersId) => {
|
||||||
try {
|
try {
|
||||||
const events = await db
|
const events = await db
|
||||||
|
|
@ -256,6 +281,21 @@ export const removeEventFromQueue = async (eventId: EventQueueEventId) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
export const clearEmailQueue = async () => {
|
||||||
|
try {
|
||||||
|
await db
|
||||||
|
.deleteFrom("event_queue")
|
||||||
|
.where(sql`data->>'tipo'`, "=", "email")
|
||||||
|
.execute();
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Error while clearing email events from queue, ${(e as Error).message}`,
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
export const clearUserEmailQueue = async (userId: UsersId) => {
|
export const clearUserEmailQueue = async (userId: UsersId) => {
|
||||||
try {
|
try {
|
||||||
await db
|
await db
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,8 @@
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import type { Flags, FlagsId } from "~/schemas/public/Flags";
|
import type { Flags, FlagsId } from "~/schemas/public/Flags";
|
||||||
import type { Querier } from "~/server/db";
|
import { db } from "~/server/db";
|
||||||
|
|
||||||
export const EditFlagHandler = async ({
|
export const EditFlagHandler = async (input: Flags) => {
|
||||||
db,
|
|
||||||
input,
|
|
||||||
}: {
|
|
||||||
db: Querier;
|
|
||||||
input: Flags;
|
|
||||||
}) => {
|
|
||||||
try {
|
try {
|
||||||
return await db
|
return await db
|
||||||
.updateTable("flags")
|
.updateTable("flags")
|
||||||
|
|
@ -25,7 +19,7 @@ export const EditFlagHandler = async ({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const GetFlagHandler = async ({ db }: { db: Querier }) => {
|
export const GetFlagHandler = async () => {
|
||||||
try {
|
try {
|
||||||
return await db.selectFrom("flags").selectAll().execute();
|
return await db.selectFrom("flags").selectAll().execute();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -36,13 +30,7 @@ export const GetFlagHandler = async ({ db }: { db: Querier }) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GetFlagValueHandler = async ({
|
export const GetFlagValueHandler = async (id: string) => {
|
||||||
db,
|
|
||||||
id,
|
|
||||||
}: {
|
|
||||||
db: Querier;
|
|
||||||
id: string;
|
|
||||||
}) => {
|
|
||||||
try {
|
try {
|
||||||
const flag = await db
|
const flag = await db
|
||||||
.selectFrom("flags")
|
.selectFrom("flags")
|
||||||
|
|
@ -58,13 +46,7 @@ export const GetFlagValueHandler = async ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RemoveFlagHandler = async ({
|
export const RemoveFlagHandler = async (id: FlagsId) => {
|
||||||
db,
|
|
||||||
id,
|
|
||||||
}: {
|
|
||||||
db: Querier;
|
|
||||||
id: FlagsId;
|
|
||||||
}) => {
|
|
||||||
try {
|
try {
|
||||||
await db.deleteFrom("flags").where("id", "=", id).execute();
|
await db.deleteFrom("flags").where("id", "=", id).execute();
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -75,13 +57,7 @@ export const RemoveFlagHandler = async ({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const AddFlagHandler = async ({
|
export const AddFlagHandler = async (input: Flags) => {
|
||||||
db,
|
|
||||||
input,
|
|
||||||
}: {
|
|
||||||
db: Querier;
|
|
||||||
input: Flags;
|
|
||||||
}) => {
|
|
||||||
try {
|
try {
|
||||||
await db
|
await db
|
||||||
.insertInto("flags")
|
.insertInto("flags")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { env } from "~/env";
|
import { env } from "~/env";
|
||||||
import { GetFlagValueHandler } from "~/server/controllers/flags.controller";
|
import { GetFlagValueHandler } from "~/server/controllers/flags.controller";
|
||||||
import { db } from "~/server/db";
|
|
||||||
import { NewMail } from "~/server/services/mailer";
|
import { NewMail } from "~/server/services/mailer";
|
||||||
|
|
||||||
const BASEURL = "https://api.skebby.it/API/v1.0/REST/";
|
const BASEURL = "https://api.skebby.it/API/v1.0/REST/";
|
||||||
|
|
@ -131,10 +130,7 @@ async function sendSMS(auth: Auth, smsData: SMSData) {
|
||||||
};*/
|
};*/
|
||||||
|
|
||||||
export const SendMessage = async (data: MinimalSMSData) => {
|
export const SendMessage = async (data: MinimalSMSData) => {
|
||||||
const smsFlag = await GetFlagValueHandler({
|
const smsFlag = await GetFlagValueHandler("SMS_ENABLED");
|
||||||
db: db,
|
|
||||||
id: "SMS_ENABLED",
|
|
||||||
});
|
|
||||||
if (smsFlag) {
|
if (smsFlag) {
|
||||||
try {
|
try {
|
||||||
const auth = await login(env.SKEBBY_USER, env.SKEBBY_PASS);
|
const auth = await login(env.SKEBBY_USER, env.SKEBBY_PASS);
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,51 @@ export const storeEmail = async ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getEmails = async ({ userId }: { userId: UsersId }) => {
|
export const getEmails = async () => {
|
||||||
|
try {
|
||||||
|
const mails = await db
|
||||||
|
.selectFrom("emails")
|
||||||
|
.innerJoin("users", "users.id", "emails.user_id")
|
||||||
|
.selectAll("emails")
|
||||||
|
.select("users.username")
|
||||||
|
.orderBy("emails.created_at", "desc")
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
const gen = await Promise.all(
|
||||||
|
mails.map(async (mail) => {
|
||||||
|
return {
|
||||||
|
...(await genMail(mail.data as MailsTemplates)),
|
||||||
|
created_at: mail.created_at,
|
||||||
|
id_email: mail.id_email,
|
||||||
|
user: mail.username,
|
||||||
|
userId: mail.user_id,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const obsoleteMails = gen.filter((mail) => mail.html === "");
|
||||||
|
|
||||||
|
if (obsoleteMails.length > 0) {
|
||||||
|
await db
|
||||||
|
.deleteFrom("emails")
|
||||||
|
.where(
|
||||||
|
"id_email",
|
||||||
|
"in",
|
||||||
|
obsoleteMails.map((mail) => mail.id_email),
|
||||||
|
)
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
return gen;
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Errore durante il recupero delle email: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUserEmails = async ({ userId }: { userId: UsersId }) => {
|
||||||
try {
|
try {
|
||||||
const mails = await db
|
const mails = await db
|
||||||
.selectFrom("emails")
|
.selectFrom("emails")
|
||||||
|
|
@ -65,7 +109,7 @@ export const getEmails = async ({ userId }: { userId: UsersId }) => {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
message: `Errore durante il recupero delle email: ${(e as Error).message}`,
|
message: `Errore durante il recupero delle email utente: ${(e as Error).message}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import type { JSX } from "react";
|
||||||
import { env } from "~/env";
|
import { env } from "~/env";
|
||||||
import type { UsersId } from "~/schemas/public/Users";
|
import type { UsersId } from "~/schemas/public/Users";
|
||||||
import { GetFlagValueHandler } from "~/server/controllers/flags.controller";
|
import { GetFlagValueHandler } from "~/server/controllers/flags.controller";
|
||||||
import { db } from "~/server/db";
|
|
||||||
import { storeEmail } from "~/server/services/email.service";
|
import { storeEmail } from "~/server/services/email.service";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -147,10 +146,7 @@ export const genMail = async ({ ...mail }: MailsTemplates) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NewMail = async ({ template, userId, mail }: NewMailProps) => {
|
export const NewMail = async ({ template, userId, mail }: NewMailProps) => {
|
||||||
const emailFlag = await GetFlagValueHandler({
|
const emailFlag = await GetFlagValueHandler("EMAIL_ENABLED");
|
||||||
db: db,
|
|
||||||
id: "EMAIL_ENABLED",
|
|
||||||
});
|
|
||||||
if (emailFlag) {
|
if (emailFlag) {
|
||||||
try {
|
try {
|
||||||
if (template) {
|
if (template) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue