feat: refactor diploma dialogs to improve structure and user experience, including enhanced layout and state management

This commit is contained in:
BenitoBB 2025-05-19 08:31:49 -06:00
parent a2a0b9631a
commit d2c7bddf18
3 changed files with 85 additions and 141 deletions

View File

@ -1,9 +1,10 @@
import { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
DialogDescription,
DialogFooter, DialogFooter,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -15,7 +16,7 @@ const DISEÑOS = [
{ id: 2, nombre: "Diseño Moderno" }, { id: 2, nombre: "Diseño Moderno" },
]; ];
function CrearDiplomaDialog({ export default function CrearDiplomaDialog({
open, open,
onOpenChange, onOpenChange,
alumno, alumno,
@ -26,13 +27,13 @@ function CrearDiplomaDialog({
fecha, fecha,
setFecha, setFecha,
}) { }) {
const [diseñoSeleccionado, setDiseñoSeleccionado] = useState( const [diseñoSeleccionado, setDiseñoSeleccionado] = useState(DISEÑOS[0]?.id || null);
DISEÑOS[0]?.id || null
);
const toggleCompetencia = (id) => { const toggleCompetencia = (id) => {
setCompetenciasAcreditadas((prev) => setCompetenciasAcreditadas((prev) =>
prev.includes(id) ? prev.filter((cid) => cid !== id) : [...prev, id] prev.includes(id)
? prev.filter((cid) => cid !== id)
: [...prev, id]
); );
}; };
@ -43,64 +44,61 @@ function CrearDiplomaDialog({
.select("competencia(id, descripcion)") .select("competencia(id, descripcion)")
.eq("curso_id", alumno.curso.id) .eq("curso_id", alumno.curso.id)
.then(({ data }) => { .then(({ data }) => {
const comps = data?.map((c) => c.competencia) || []; const comps = data?.map((c) => c.competencia).filter(Boolean) || [];
setCompetencias(comps); setCompetencias(comps);
setCompetenciasAcreditadas(comps.map((c) => c.id)); // todas seleccionadas por default setCompetenciasAcreditadas(comps.map((c) => c.id)); // todas seleccionadas por default
}); });
} }
}, [alumno]); }, [alumno, setCompetencias, setCompetenciasAcreditadas]);
const handleCrearDiploma = async () => { const handleCrearDiploma = async () => {
// Aquí va la lógica para crear el diploma en la base de datos // Aquí va la lógica para crear el diploma en la base de datos
// 1. Insertar en Diploma
// 2. Insertar en Diploma_Competencia
// 3. (Opcional) Generar PDF y enviar
// Ejemplo de inserción (ajusta según tu lógica real)
// await supabaseClient.from("Diploma").insert({...});
// await supabaseClient.from("Diploma_Competencia").insert([...]);
onOpenChange(false); onOpenChange(false);
// Aquí puedes mostrar un mensaje de éxito o iniciar la generación del PDF
}; };
if (!alumno) return null; if (!open || !alumno) return null;
return ( return (
<div className="text-black p-4"> <Dialog open={open} onOpenChange={onOpenChange}>
<h2 className="text-xl font-bold mb-4">Crear Diploma</h2> <DialogContent className="w-[400px]">
<div className="mb-2"> <DialogHeader>
<label className="block text-sm font-semibold mb-1"> <DialogTitle>Crear Diploma</DialogTitle>
Competencias: <DialogDescription>
</label> Selecciona las competencias acreditadas para el diploma.
{competencias.map((comp) => ( </DialogDescription>
<div key={comp.id} className="flex items-center mb-1"> </DialogHeader>
<input <div className="mb-2">
type="checkbox" <label className="block text-sm font-semibold mb-1">Competencias:</label>
checked={competenciasAcreditadas.includes(comp.id)} {competencias.map((comp) => (
onChange={() => toggleCompetencia(comp.id)} <div key={comp.id} className="flex items-center mb-1">
className="mr-2" <input
/> type="checkbox"
<span>{comp.descripcion}</span> checked={competenciasAcreditadas.includes(comp.id)}
</div> onChange={() => toggleCompetencia(comp.id)}
))} className="mr-2"
</div> />
<div className="w-full flex justify-around"> <span>{comp.descripcion}</span>
<Button </div>
onClick={handleCrearDiploma} ))}
className="bg-green-500 hover:bg-green-700 text-white" </div>
> <div className="mb-2">
Crear Diploma <label className="block text-sm font-semibold mb-1">Fecha:</label>
</Button> <input
<Button type="date"
onClick={() => onOpenChange(false)} className="border p-1 w-full"
className="bg-gray-400 hover:bg-gray-600 text-white" value={fecha}
> onChange={e => setFecha(e.target.value)}
Cancelar />
</Button> </div>
</div> <DialogFooter>
</div> <Button onClick={handleCrearDiploma} className="bg-green-500 hover:bg-green-700 text-white">
Crear Diploma
</Button>
<Button onClick={() => onOpenChange(false)} className="bg-gray-400 hover:bg-gray-600 text-white">
Cancelar
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
); );
} }
export default CrearDiplomaDialog;

View File

@ -19,12 +19,14 @@ function VistaPreviaDiplomaDialog({
}) { }) {
const [mostrarVistaPrevia, setMostrarVistaPrevia] = useState(false); const [mostrarVistaPrevia, setMostrarVistaPrevia] = useState(false);
if (!alumno) return null; if (!open || !alumno) return null;
const competenciasMostradas = // Filtra solo las competencias seleccionadas si se pasa el array de acreditadas
Array.isArray(competenciasAcreditadas) && competenciasAcreditadas.length > 0 const competenciasMostradas = Array.isArray(competenciasAcreditadas)
? competencias.filter((comp) => competenciasAcreditadas.includes(comp.id)) ? (Array.isArray(competencias) ? competencias : []).filter((comp) =>
: []; competenciasAcreditadas.includes(comp.id)
)
: (Array.isArray(competencias) ? competencias : []);
const competenciasParaPDF = const competenciasParaPDF =
Array.isArray(competenciasMostradas) && competenciasMostradas.length > 0 Array.isArray(competenciasMostradas) && competenciasMostradas.length > 0
@ -32,8 +34,8 @@ function VistaPreviaDiplomaDialog({
: []; : [];
return ( return (
<div className="text-black p-4"> <div className="bg-white border shadow-lg rounded p-8 w-[500px] h-[350px] mx-4 flex flex-col text-black">
<h2 className="text-xl font-bold mb-4">Diploma</h2> <h2 className="text-2xl font-bold text-center mb-6">Diploma</h2>
<div className="text-lg mb-2"> <div className="text-lg mb-2">
<b>Alumno:</b> {alumno.nombre} <b>Alumno:</b> {alumno.nombre}
</div> </div>
@ -51,61 +53,9 @@ function VistaPreviaDiplomaDialog({
<div className="text-lg mb-2"> <div className="text-lg mb-2">
<b>Fecha:</b> {fecha || new Date().toLocaleDateString()} <b>Fecha:</b> {fecha || new Date().toLocaleDateString()}
</div> </div>
<div className="mt-4 flex justify-around"> <div className="mt-auto text-gray-400 text-xs text-right">
<PDFDownloadLink Vista previa
document={
<Diploma
alumno={alumno}
curso={alumno.curso}
competencias={competenciasParaPDF}
fecha={fecha || new Date().toLocaleDateString()}
/>
}
fileName={`Diploma_${alumno.nombre}.pdf`}
>
{({ loading }) =>
loading ? (
<Button className="bg-gray-300 px-4 py-2 rounded" disabled>
Generando PDF...
</Button>
) : (
<Button className="bg-green-500 hover:bg-green-700 text-white px-4 py-2 rounded">
Descargar PDF
</Button>
)
}
</PDFDownloadLink>
<Button
className="bg-blue-500 hover:bg-blue-700 text-white px-4 py-2 rounded"
onClick={() => setMostrarVistaPrevia(true)}
>
Vista previa
</Button>
</div> </div>
{/* Vista previa del 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] mb-4 border">
<PDFViewer width="100%" height="100%">
<Diploma
alumno={alumno}
curso={alumno.curso}
competencias={competenciasParaPDF}
fecha={fecha || new Date().toLocaleDateString()}
/>
</PDFViewer>
</div>
<Button
className="bg-red-500 hover:bg-red-700 text-white px-4 py-2 rounded"
onClick={() => setMostrarVistaPrevia(false)}
>
Cerrar vista previa
</Button>
</div>
</div>
)}
</div> </div>
); );
} }

View File

@ -4,7 +4,6 @@ import { supabaseClient } from "@/utils/supabase";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import CrearDiplomaDialog from "@/components/dialogs/crearDiplomaDialog"; import CrearDiplomaDialog from "@/components/dialogs/crearDiplomaDialog";
import VistaPreviaDiplomaDialog from "@/components/dialogs/vistaPreviaDiplomaDialog"; import VistaPreviaDiplomaDialog from "@/components/dialogs/vistaPreviaDiplomaDialog";
import { Dialog, DialogContent } from "@/components/ui/dialog";
export default function DiplomasVista() { export default function DiplomasVista() {
const [alumnos, setAlumnos] = useState([]); const [alumnos, setAlumnos] = useState([]);
@ -80,33 +79,30 @@ export default function DiplomasVista() {
</table> </table>
</div> </div>
{/* Dialog para crear diploma y vista previa juntos */} {/* Dialog para crear diploma y vista previa juntos */}
<Dialog open={mostrarDialog} onOpenChange={handleCloseDialog}> {mostrarDialog && (
<DialogContent className="min-w-4xl"> <div className="fixed inset-0 z-50 flex items-center justify-center">
<div className="flex flex-row gap-8"> <div className="flex gap-8 bg-black bg-opacity-30 p-8 rounded">
<div className="w-[350px]"> <CrearDiplomaDialog
<CrearDiplomaDialog open={mostrarDialog}
onOpenChange={handleCloseDialog} onOpenChange={handleCloseDialog}
alumno={alumnoSeleccionado} alumno={alumnoSeleccionado}
competencias={competencias} competencias={competencias}
setCompetencias={setCompetencias} setCompetencias={setCompetencias}
competenciasAcreditadas={competenciasAcreditadas} competenciasAcreditadas={competenciasAcreditadas}
setCompetenciasAcreditadas={setCompetenciasAcreditadas} setCompetenciasAcreditadas={setCompetenciasAcreditadas}
fecha={fecha} fecha={fecha}
setFecha={setFecha} setFecha={setFecha}
/> />
</div> <VistaPreviaDiplomaDialog
<div className="w-[400px]"> open={mostrarDialog}
<VistaPreviaDiplomaDialog alumno={alumnoSeleccionado}
onOpenChange={handleCloseDialog} competencias={competencias}
alumno={alumnoSeleccionado} fecha={fecha}
competencias={competencias} competenciasAcreditadas={competenciasAcreditadas}
fecha={fecha} />
competenciasAcreditadas={competenciasAcreditadas}
/>
</div>
</div> </div>
</DialogContent> </div>
</Dialog> )}
</Layout> </Layout>
); );
} }