diff --git a/diplomas/public/encabezado.png b/diplomas/public/encabezado.png new file mode 100644 index 0000000..beb13ec Binary files /dev/null and b/diplomas/public/encabezado.png differ diff --git a/diplomas/src/components/Diploma.jsx b/diplomas/src/components/Diploma.jsx index 7325af6..5c2af03 100644 --- a/diplomas/src/components/Diploma.jsx +++ b/diplomas/src/components/Diploma.jsx @@ -1,10 +1,29 @@ import React from "react"; -import { Document, Page, Text, View, StyleSheet } from "@react-pdf/renderer"; +import { + Document, + Page, + Text, + View, + StyleSheet, + Image, +} from "@react-pdf/renderer"; const styles = StyleSheet.create({ - page: { padding: 40, fontFamily: "Helvetica" }, + page: { fontFamily: "Helvetica" }, title: { fontSize: 24, textAlign: "center", marginBottom: 20 }, - section: { marginBottom: 10, fontSize: 14 }, + nombre: { + fontSize: 18, + textAlign: "center", + marginBottom: 10, + fontStyle: "italic", + }, + curso: { + fontSize: 30, + textAlign: "center", + marginBottom: 10, + fontWeight: "bold", + }, + section: { padding: 40, fontSize: 14 }, competencias: { marginLeft: 20, marginTop: 5 }, competencia: { fontSize: 12, marginBottom: 2 }, footer: { @@ -20,36 +39,21 @@ export default function Diploma({ alumno, curso, competencias = [], fecha }) { return ( - Diploma - - - Alumno: - {alumno?.nombre} - - - - - Curso: - {curso?.nombre || "Sin curso"} - - - - Competencias Acreditadas: - - {(competencias || []).map((comp) => ( - - - {comp.descripcion} - - ))} - - - - - Fecha: - {fecha} - - - Generado por SIDAC + + Otorga la presente + CONSTANCIA + a: + {alumno?.nombre} + + Por su asistencia a la píldora educativa + + {curso?.nombre || "Sin curso"} + + con duración de 2 horas, modalidad remota + + + Se expide en la ciudad de Xalapa, Ver., {fecha} + ); diff --git a/diplomas/src/components/app-sidebar.jsx b/diplomas/src/components/app-sidebar.jsx index 62d21dc..1d5afbe 100644 --- a/diplomas/src/components/app-sidebar.jsx +++ b/diplomas/src/components/app-sidebar.jsx @@ -36,6 +36,15 @@ const data = { }, ], }, + /*{ + title: "Vista general", + items: [ + { + title: "Vista general", + url: "/vistaGeneral", + }, + ], + },*/ { title: "Cursos", items: [ @@ -53,6 +62,40 @@ const data = { }, ], }, + { + title: "Inyecciones", + items: [ + { + title: "Vista de inyecciones", + url: "/inyeccionesVista", + }, + { + title: "Agregar manualmente", + url: "/inyeccionesManual", + }, + { + title: "Agregar desde archivo", + url: "/inyeccionesArchivo", + }, + ], + }, + { + title: "Pildoras", + items: [ + { + title: "Vista de pildoras", + url: "/pildorasVista", + }, + { + title: "Agregar manualmente", + url: "/pildorasManual", + }, + { + title: "Agregar desde archivo", + url: "/pildorasArchivo", + }, + ], + }, { title: "Diplomas", items: [ diff --git a/diplomas/src/components/cursosManualForm.jsx b/diplomas/src/components/cursosManualForm.jsx new file mode 100644 index 0000000..14719c1 --- /dev/null +++ b/diplomas/src/components/cursosManualForm.jsx @@ -0,0 +1,290 @@ +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { cursosSchema } from "@/schemas/CursosSchema"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { supabaseClient } from "@/utils/supabase"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from "@/components/ui/dialog"; + +export function CursosManualForm({ nombreSugerido = "" }) { + const [addCompetencia, setAddCompetencia] = useState(false); + const [competenciasGuardadas, setCompetenciasGuardadas] = useState([]); + const [loading, setLoading] = useState(false); + const [mostrarDialog, setMostrarDialog] = useState(false); + const [mensajeDialog, setMensajeDialog] = useState(""); + const [mostrarDialogCompetencia, setMostrarDialogCompetencia] = useState(false); + + const form = useForm({ + resolver: zodResolver(cursosSchema), + defaultValues: { + nombre: nombreSugerido, + descripcion: "", + horas: 0, + nuevaCompetencia: "", + }, + }); + + const { + register, + handleSubmit, + setValue, + getValues, + formState: { errors }, + } = form; + + const handleSaveCompetencia = async (e) => { + e.preventDefault(); + const nuevaCompetencia = getValues("nuevaCompetencia").trim(); + if (!nuevaCompetencia) return; + + if (competenciasGuardadas.some((c) => c.descripcion === nuevaCompetencia)) { + alert("La competencia ya fue agregada."); + return; + } + + let competenciaId = null; + try { + const { data: existente } = await supabaseClient + .from("competencia") + .select("id") + .eq("descripcion", nuevaCompetencia) + .maybeSingle(); + + if (existente && existente.id) { + competenciaId = existente.id; + } else { + const { data: insertada, error } = await supabaseClient + .from("competencia") + .insert([{ descripcion: nuevaCompetencia }]) + .select("id") + .single(); + if (error) throw error; + competenciaId = insertada.id; + } + + setCompetenciasGuardadas([ + ...competenciasGuardadas, + { id: competenciaId, descripcion: nuevaCompetencia }, + ]); + setAddCompetencia(false); + setValue("nuevaCompetencia", ""); + setMostrarDialogCompetencia(true); + } catch (err) { + alert("Error al guardar la competencia: " + (err.message || err)); + } + }; + + const handleDeleteCompetencia = (index) => { + setCompetenciasGuardadas( + competenciasGuardadas.filter((_, i) => i !== index) + ); + }; + + const onSubmit = async (data) => { + const { nombre, descripcion } = data; + const horas = parseInt(data.horas, 10); + + setLoading(true); + + try { + const { data: cursoInsertado, error: errorCurso } = await supabaseClient + .from("curso") + .insert([{ nombre, descripcion, horas }]) + .select("id") + .single(); + + if (errorCurso) { + setMensajeDialog("Error al guardar el curso: " + errorCurso.message); + setMostrarDialog(true); + setLoading(false); + return; + } + + const cursoId = cursoInsertado.id; + const relaciones = competenciasGuardadas.map((c) => ({ + curso_id: cursoId, + competencia_id: c.id, + })); + + if (relaciones.length > 0) { + const { error: errorPivote } = await supabaseClient + .from("curso_competencia") + .insert(relaciones); + if (errorPivote) { + setMensajeDialog( + "Error al asociar competencias: " + errorPivote.message + ); + setMostrarDialog(true); + setLoading(false); + return; + } + } + + setMensajeDialog("Curso guardado exitosamente"); + setMostrarDialog(true); + form.reset(); + setCompetenciasGuardadas([]); + } catch (err) { + alert("Ocurrió un error inesperado"); + } finally { + setLoading(false); + } + }; + + return ( +
+ + {errors.nombre && ( +

{errors.nombre.message}

+ )} + +