feat: add custom message functionality for diploma emails and WhatsApp notifications
This commit is contained in:
parent
cddb2291c3
commit
c747b4615c
|
@ -8,11 +8,16 @@ import {
|
|||
import Diploma from "@/components/Diploma";
|
||||
import { PDFDownloadLink, PDFViewer, pdf } from "@react-pdf/renderer";
|
||||
import { supabaseClient } from "@/utils/supabase";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { mensajesSchema } from "@/schemas/mensajesSchema";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
|
||||
function VistaPreviaDiplomaDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
alumno,
|
||||
curso,
|
||||
competencias: competenciasProp,
|
||||
fecha,
|
||||
competenciasAcreditadas,
|
||||
|
@ -20,8 +25,77 @@ function VistaPreviaDiplomaDialog({
|
|||
const [mostrarVistaPrevia, setMostrarVistaPrevia] = useState(false);
|
||||
const [enviando, setEnviando] = useState(false);
|
||||
const [mensaje, setMensaje] = useState("");
|
||||
const [loadingMensajes, setLoadingMensajes] = useState(false);
|
||||
const [competencias, setCompetencias] = useState([]);
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(mensajesSchema),
|
||||
defaultValues: {
|
||||
correo: "",
|
||||
whatsapp: "",
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
getValues,
|
||||
formState: { errors },
|
||||
} = form;
|
||||
|
||||
// 🔄 Cargar mensajes al abrir el modal
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setLoadingMensajes(true);
|
||||
Promise.all([
|
||||
supabaseClient.from("mensaje_correo").select("id, mensaje").single(),
|
||||
supabaseClient.from("mensaje_whatsapp").select("id, mensaje").single(),
|
||||
])
|
||||
.then(([correoRes, whatsappRes]) => {
|
||||
if (correoRes.data) setValue("correo", correoRes.data.mensaje);
|
||||
if (whatsappRes.data) setValue("whatsapp", whatsappRes.data.mensaje);
|
||||
})
|
||||
.finally(() => setLoadingMensajes(false));
|
||||
}
|
||||
}, [open, setValue]);
|
||||
|
||||
// 📝 Guardar mensajes personalizados
|
||||
const handleGuardarMensajes = async () => {
|
||||
const { correo, whatsapp } = getValues();
|
||||
|
||||
const [{ data: correoExistente }] = await Promise.all([
|
||||
supabaseClient.from("mensaje_correo").select("id").maybeSingle(),
|
||||
]);
|
||||
|
||||
if (correoExistente) {
|
||||
await supabaseClient
|
||||
.from("mensaje_correo")
|
||||
.update({ mensaje: correo })
|
||||
.eq("id", correoExistente.id);
|
||||
} else {
|
||||
await supabaseClient.from("mensaje_correo").insert({ mensaje: correo });
|
||||
}
|
||||
|
||||
const { data: whatsappExistente } = await supabaseClient
|
||||
.from("mensaje_whatsapp")
|
||||
.select("id")
|
||||
.maybeSingle();
|
||||
|
||||
if (whatsappExistente) {
|
||||
await supabaseClient
|
||||
.from("mensaje_whatsapp")
|
||||
.update({ mensaje: whatsapp })
|
||||
.eq("id", whatsappExistente.id);
|
||||
} else {
|
||||
await supabaseClient
|
||||
.from("mensaje_whatsapp")
|
||||
.insert({ mensaje: whatsapp });
|
||||
}
|
||||
|
||||
setMensaje("Mensajes guardados correctamente.");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (alumno && alumno.curso?.id) {
|
||||
supabaseClient
|
||||
|
@ -41,28 +115,25 @@ function VistaPreviaDiplomaDialog({
|
|||
? competencias.filter((comp) => competenciasAcreditadas.includes(comp.id))
|
||||
: competencias;
|
||||
|
||||
// Simulación de envío de PDF por correo y WhatsApp
|
||||
const handleEnviar = async () => {
|
||||
setEnviando(true);
|
||||
setMensaje("");
|
||||
// Genera el PDF como blob
|
||||
|
||||
const blob = await pdf(
|
||||
<Diploma
|
||||
alumno={alumno}
|
||||
curso={alumno.curso}
|
||||
curso={curso}
|
||||
competencias={competenciasMostradas}
|
||||
fecha={fecha || new Date().toLocaleDateString()}
|
||||
/>
|
||||
).toBlob();
|
||||
|
||||
// Convierte el blob a base64
|
||||
const pdfBase64 = await new Promise((resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result.split(",")[1]);
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
|
||||
// Llama a tu API de Next.js
|
||||
const resp = await fetch("/api/send-diploma", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
@ -71,32 +142,30 @@ function VistaPreviaDiplomaDialog({
|
|||
nombre: alumno.nombre,
|
||||
curso: alumno.curso?.nombre || "Sin curso",
|
||||
pdfBase64,
|
||||
mensajeCorreo: getValues("correo"),
|
||||
}),
|
||||
});
|
||||
|
||||
if (resp.ok) {
|
||||
// WhatsApp real (abre ventana)
|
||||
const telefono = alumno.telefono.replace(/\D/g, "");
|
||||
const mensajeWhatsapp = encodeURIComponent(
|
||||
`Hola ${alumno.nombre}, tu diploma ha sido generado y enviado a tu correo (${alumno.correo}). ¡Felicidades!`
|
||||
);
|
||||
const mensajeWhatsapp = encodeURIComponent(getValues("whatsapp"));
|
||||
window.open(
|
||||
`https://wa.me/${telefono}?text=${mensajeWhatsapp}`,
|
||||
"_blank"
|
||||
);
|
||||
|
||||
setMensaje(
|
||||
`Diploma enviado por correo a ${alumno.correo} y mensaje enviado por WhatsApp al ${alumno.telefono}.`
|
||||
);
|
||||
setMensaje(`Diploma enviado por correo a ${alumno.correo}.`);
|
||||
} else {
|
||||
setMensaje("Error enviando el diploma por correo.");
|
||||
setMensaje("Error enviando el diploma.");
|
||||
}
|
||||
|
||||
setEnviando(false);
|
||||
};
|
||||
|
||||
if (!alumno) return null;
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-lg text-black">
|
||||
<DialogContent className="max-w-lg h-screen text-black overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Diploma</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
@ -117,15 +186,35 @@ function VistaPreviaDiplomaDialog({
|
|||
<div className="text-lg mb-2">
|
||||
<b>Fecha:</b> {fecha || new Date().toLocaleDateString()}
|
||||
</div>
|
||||
<div className="mt-auto text-gray-400 text-xs text-right">
|
||||
Vista previa
|
||||
</div>
|
||||
<div className="mt-4 flex gap-2 justify-center flex-wrap">
|
||||
{/* Campos de mensaje */}
|
||||
<form>
|
||||
<div className="mb-4">
|
||||
<Textarea
|
||||
label="Mensaje para correo"
|
||||
placeholder="Escribe un mensaje personalizado para el correo"
|
||||
{...register("correo")}
|
||||
error={errors.correo?.message}
|
||||
disabled={loadingMensajes}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Textarea
|
||||
label="Mensaje para WhatsApp"
|
||||
placeholder="Escribe un mensaje personalizado para WhatsApp"
|
||||
{...register("whatsapp")}
|
||||
error={errors.whatsapp?.message}
|
||||
disabled={loadingMensajes}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* Acciones */}
|
||||
<div className="mt-4 flex flex-wrap gap-2 justify-center">
|
||||
<PDFDownloadLink
|
||||
document={
|
||||
<Diploma
|
||||
alumno={alumno}
|
||||
curso={alumno.curso}
|
||||
curso={curso}
|
||||
competencias={competenciasMostradas}
|
||||
fecha={fecha || new Date().toLocaleDateString()}
|
||||
/>
|
||||
|
@ -144,12 +233,14 @@ function VistaPreviaDiplomaDialog({
|
|||
)
|
||||
}
|
||||
</PDFDownloadLink>
|
||||
|
||||
<button
|
||||
className="bg-blue-500 hover:bg-blue-700 text-white px-4 py-2 rounded"
|
||||
onClick={() => setMostrarVistaPrevia(true)}
|
||||
>
|
||||
Ver vista previa PDF
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="bg-purple-600 hover:bg-purple-800 text-white px-4 py-2 rounded"
|
||||
onClick={handleEnviar}
|
||||
|
@ -157,20 +248,31 @@ function VistaPreviaDiplomaDialog({
|
|||
>
|
||||
{enviando ? "Enviando..." : "Enviar por correo y WhatsApp"}
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="bg-yellow-500 hover:bg-yellow-600 text-white px-4 py-2 rounded"
|
||||
onClick={handleGuardarMensajes}
|
||||
type="button"
|
||||
>
|
||||
Guardar mensajes
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{mensaje && (
|
||||
<div className="mt-4 text-green-700 font-semibold text-center">
|
||||
{mensaje}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Vista previa PDF */}
|
||||
{mostrarVistaPrevia && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-60 flex flex-col items-center justify-center z-50">
|
||||
<div className="bg-white rounded shadow-lg p-4 flex flex-col items-center">
|
||||
<div className="w-[80vw] h-[80vh] lg:h-[90vh] mb-4 border">
|
||||
<div className="fixed inset-0 bg-black bg-opacity-60 flex items-center justify-center z-50">
|
||||
<div className="bg-white rounded shadow-lg p-4">
|
||||
<div className="w-[80vw] h-[90vh] mb-4 border">
|
||||
<PDFViewer width="100%" height="100%">
|
||||
<Diploma
|
||||
alumno={alumno}
|
||||
curso={alumno.curso}
|
||||
curso={curso}
|
||||
competencias={competenciasMostradas}
|
||||
fecha={fecha || new Date().toLocaleDateString()}
|
||||
/>
|
||||
|
|
|
@ -5,14 +5,18 @@ sgMail.setApiKey(process.env.SENDGRID_API_KEY);
|
|||
export default async function handler(req, res) {
|
||||
if (req.method !== "POST") return res.status(405).end();
|
||||
|
||||
const { email, nombre, curso, pdfBase64 } = req.body;
|
||||
// Recibe mensajeCorreo desde el body
|
||||
const { email, nombre, curso, pdfBase64, mensajeCorreo } = req.body;
|
||||
|
||||
try {
|
||||
await sgMail.send({
|
||||
to: email,
|
||||
from: "rviverosgonzalez@outlook.com", // Cambia esto por tu correo verificado en SendGrid
|
||||
subject: "Tu diploma",
|
||||
text: `Hola ${nombre}, has concluido tu curso ${curso} por lo que adjuntamos tu diploma.`,
|
||||
// Usa el mensajeCorreo personalizado, si no viene usa el texto por defecto
|
||||
text:
|
||||
mensajeCorreo ||
|
||||
`Hola ${nombre}, has concluido tu curso ${curso} por lo que adjuntamos tu diploma.`,
|
||||
attachments: [
|
||||
{
|
||||
content: pdfBase64,
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const mensajesSchema = z.object({
|
||||
correo: z.string().min(1, "El mensaje de correo es obligatorio"),
|
||||
whatsapp: z.string().min(1, "El mensaje de WhatsApp es obligatorio"),
|
||||
});
|
Loading…
Reference in New Issue