feat: update Alumnos schema to clarify type selection and enhance data loading in AlumnosVista and AlumnosManual

This commit is contained in:
SirRobert-1 2025-06-13 20:41:18 -06:00
parent 814354baac
commit d7562fa4ab
3 changed files with 197 additions and 59 deletions

View File

@ -11,7 +11,6 @@ import {
} from "@/components/ui/select"; } from "@/components/ui/select";
import { import {
Dialog, Dialog,
DialogTrigger,
DialogContent, DialogContent,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
@ -24,61 +23,99 @@ import { alumnoSchema } from "@/schemas/AlumnosSchema";
export default function AlumnosManual() { export default function AlumnosManual() {
const [cursos, setCursos] = useState([]); const [cursos, setCursos] = useState([]);
const [isDialogOpen, setIsDialogOpen] = useState(false); // Estado para controlar el diálogo const [inyecciones, setInyecciones] = useState([]);
const [pildoras, setPildoras] = useState([]);
const [isDialogOpen, setIsDialogOpen] = useState(false);
// Configurar React Hook Form con zod
const { const {
register, register,
handleSubmit, handleSubmit,
setValue, setValue,
watch,
formState: { errors }, formState: { errors },
reset, reset,
clearErrors,
} = useForm({ } = useForm({
resolver: zodResolver(alumnoSchema), resolver: zodResolver(alumnoSchema),
defaultValues: { defaultValues: {
nombre: "", nombre: "",
correo: "", correo: "",
telefono: "", telefono: "",
tipo: "",
cursoSeleccionado: "", cursoSeleccionado: "",
}, },
}); });
// Cargar cursos al iniciar el componente const tipo = watch("tipo");
const cursoSeleccionado = watch("cursoSeleccionado");
// Cargar opciones según tipo seleccionado
useEffect(() => { useEffect(() => {
const cargarCursos = async () => { if (!tipo) {
const { data, error } = await supabaseClient setValue("cursoSeleccionado", "");
.from("curso") clearErrors("cursoSeleccionado");
.select("id, nombre"); return;
if (error) { }
console.error("Error al cargar cursos:", error.message); setValue("cursoSeleccionado", "");
} else { clearErrors("cursoSeleccionado");
setCursos(data); if (tipo === "curso") {
} cargarCursos();
}; } else if (tipo === "inyeccion") {
cargarCursos(); cargarInyecciones();
}, []); } else if (tipo === "pildora") {
cargarPildoras();
}
// eslint-disable-next-line
}, [tipo]);
const cargarCursos = async () => {
const { data, error } = await supabaseClient
.from("curso")
.select("id, nombre");
if (!error) setCursos(data || []);
};
const cargarInyecciones = async () => {
const { data, error } = await supabaseClient
.from("inyeccion")
.select("id, nombre");
if (!error) setInyecciones(data || []);
};
const cargarPildoras = async () => {
const { data, error } = await supabaseClient
.from("pildoras")
.select("id, nombre");
if (!error) setPildoras(data || []);
};
// Guardar alumno // Guardar alumno
const manejarGuardar = async (data) => { const manejarGuardar = async (data) => {
try { try {
const { error } = await supabaseClient.from("alumno").insert([ let campos = {
{ nombre: data.nombre,
nombre: data.nombre, correo: data.correo,
correo: data.correo, telefono: data.telefono,
telefono: data.telefono, tipo_formacion: data.tipo,
curso_id: Number(data.cursoSeleccionado), // Guardar el ID del curso curso_id: null,
}, inyeccion_id: null,
]); pildoras_id: null,
};
if (data.tipo === "curso")
campos.curso_id = Number(data.cursoSeleccionado);
if (data.tipo === "inyeccion")
campos.inyeccion_id = Number(data.cursoSeleccionado);
if (data.tipo === "pildora")
campos.pildoras_id = Number(data.cursoSeleccionado);
const { error } = await supabaseClient.from("alumno").insert([campos]);
if (error) { if (error) {
console.error("Error al guardar:", error.message); setIsDialogOpen(false);
setIsDialogOpen(false); // Asegurarse de cerrar el diálogo en caso de error
} else { } else {
setIsDialogOpen(true); // Mostrar el diálogo al guardar exitosamente setIsDialogOpen(true);
reset(); // Reiniciar el formulario reset();
} }
} catch (err) { } catch (err) {
console.error("Error inesperado:", err); // Manejo de error inesperado
} }
}; };
@ -118,20 +155,77 @@ export default function AlumnosManual() {
{errors.telefono.message} {errors.telefono.message}
</p> </p>
)} )}
{/* Select para tipo de formación */}
<Select <Select
onValueChange={(value) => setValue("cursoSeleccionado", value)} value={tipo}
onValueChange={(value) => setValue("tipo", value)}
> >
<SelectTrigger className="w-full px-3 py-2 border border-gray-300 rounded-md mb-3"> <SelectTrigger className="w-full px-3 py-2 border border-gray-300 rounded-md mb-3">
<SelectValue placeholder="Selecciona un curso" /> <SelectValue placeholder="Selecciona tipo de formación" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{cursos.map((curso) => ( <SelectItem value="curso">Curso</SelectItem>
<SelectItem key={curso.id} value={curso.id.toString()}> <SelectItem value="inyeccion">Inyección</SelectItem>
{curso.nombre} <SelectItem value="pildora">Píldora</SelectItem>
</SelectItem>
))}
</SelectContent> </SelectContent>
</Select> </Select>
{errors.tipo && (
<p className="text-red-500 text-sm mt-1">{errors.tipo.message}</p>
)}
{/* Select para la opción según tipo */}
{tipo === "curso" && (
<Select
value={cursoSeleccionado}
onValueChange={(value) => setValue("cursoSeleccionado", value)}
>
<SelectTrigger className="w-full px-3 py-2 border border-gray-300 rounded-md mb-3">
<SelectValue placeholder="Selecciona un curso" />
</SelectTrigger>
<SelectContent>
{cursos.map((curso) => (
<SelectItem key={curso.id} value={curso.id.toString()}>
{curso.nombre}
</SelectItem>
))}
</SelectContent>
</Select>
)}
{tipo === "inyeccion" && (
<Select
value={cursoSeleccionado}
onValueChange={(value) => setValue("cursoSeleccionado", value)}
>
<SelectTrigger className="w-full px-3 py-2 border border-gray-300 rounded-md mb-3">
<SelectValue placeholder="Selecciona una inyección" />
</SelectTrigger>
<SelectContent>
{inyecciones.map((iny) => (
<SelectItem key={iny.id} value={iny.id.toString()}>
{iny.nombre}
</SelectItem>
))}
</SelectContent>
</Select>
)}
{tipo === "pildora" && (
<Select
value={cursoSeleccionado}
onValueChange={(value) => setValue("cursoSeleccionado", value)}
>
<SelectTrigger className="w-full px-3 py-2 border border-gray-300 rounded-md mb-3">
<SelectValue placeholder="Selecciona una píldora" />
</SelectTrigger>
<SelectContent>
{pildoras.map((p) => (
<SelectItem key={p.id} value={p.id.toString()}>
{p.nombre}
</SelectItem>
))}
</SelectContent>
</Select>
)}
{errors.cursoSeleccionado && ( {errors.cursoSeleccionado && (
<p className="text-red-500 text-sm mt-1"> <p className="text-red-500 text-sm mt-1">
{errors.cursoSeleccionado.message} {errors.cursoSeleccionado.message}

View File

@ -45,6 +45,9 @@ export default function AlumnosVista() {
useEffect(() => { useEffect(() => {
cargarAlumnos(); cargarAlumnos();
// ...cargar cursos, inyecciones, pildoras... // ...cargar cursos, inyecciones, pildoras...
cargarCursos();
cargarInyecciones();
cargarPildoras();
}, []); }, []);
const cargarCursos = async () => { const cargarCursos = async () => {
@ -56,10 +59,21 @@ export default function AlumnosVista() {
} }
}; };
const cargarInyecciones = async () => {
const { data, error } = await supabaseClient.from("inyeccion").select("*");
if (!error) setInyecciones(data || []);
};
const cargarPildoras = async () => {
const { data, error } = await supabaseClient.from("pildoras").select("*");
if (!error) setPildoras(data || []);
};
const cargarAlumnos = async () => { const cargarAlumnos = async () => {
const { data, error } = await supabaseClient const { data, error } = await supabaseClient
.from("alumno") .from("alumno")
.select(` .select(
`
id, id,
nombre, nombre,
correo, correo,
@ -71,7 +85,8 @@ export default function AlumnosVista() {
inyeccion(nombre), inyeccion(nombre),
pildoras_id, pildoras_id,
pildoras(nombre) pildoras(nombre)
`) `
)
.order("id", { ascending: true }); .order("id", { ascending: true });
setAlumnos(data || []); setAlumnos(data || []);
}; };
@ -100,9 +115,12 @@ export default function AlumnosVista() {
setNuevoNumero(alumno.telefono || ""); setNuevoNumero(alumno.telefono || "");
setNuevoTipo(alumno.tipo_formacion || ""); setNuevoTipo(alumno.tipo_formacion || "");
// Detecta la formación actual según el tipo // Detecta la formación actual según el tipo
if (alumno.tipo_formacion === "curso") setNuevaFormacion(alumno.curso_id ? String(alumno.curso_id) : ""); if (alumno.tipo_formacion === "curso")
else if (alumno.tipo_formacion === "inyeccion") setNuevaFormacion(alumno.inyeccion_id ? String(alumno.inyeccion_id) : ""); setNuevaFormacion(alumno.curso_id ? String(alumno.curso_id) : "");
else if (alumno.tipo_formacion === "pildora") setNuevaFormacion(alumno.pildoras_id ? String(alumno.pildoras_id) : ""); else if (alumno.tipo_formacion === "inyeccion")
setNuevaFormacion(alumno.inyeccion_id ? String(alumno.inyeccion_id) : "");
else if (alumno.tipo_formacion === "pildora")
setNuevaFormacion(alumno.pildoras_id ? String(alumno.pildoras_id) : "");
else setNuevaFormacion(""); else setNuevaFormacion("");
}; };
@ -126,7 +144,8 @@ export default function AlumnosVista() {
}; };
if (nuevoTipo === "curso") updateObj.curso_id = Number(nuevaFormacion); if (nuevoTipo === "curso") updateObj.curso_id = Number(nuevaFormacion);
if (nuevoTipo === "inyeccion") updateObj.inyeccion_id = Number(nuevaFormacion); if (nuevoTipo === "inyeccion")
updateObj.inyeccion_id = Number(nuevaFormacion);
if (nuevoTipo === "pildora") updateObj.pildoras_id = Number(nuevaFormacion); if (nuevoTipo === "pildora") updateObj.pildoras_id = Number(nuevaFormacion);
const { error } = await supabaseClient const { error } = await supabaseClient
@ -188,25 +207,37 @@ export default function AlumnosVista() {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{alumnos.map(alumno => {alumnos.map((alumno) =>
alumnoEditando === alumno.id ? ( alumnoEditando === alumno.id ? (
<tr key={alumno.id}> <tr key={alumno.id}>
<td className="py-2 px-4 border-b text-center"> <td className="py-2 px-4 border-b text-center">
{alumno.id} {alumno.id}
</td> </td>
<td className="py-2 px-4 border-b"> <td className="py-2 px-4 border-b">
<input value={nuevoNombre} onChange={e => setNuevoNombre(e.target.value)} className="border rounded px-2 py-1 text-black" /> <input
value={nuevoNombre}
onChange={(e) => setNuevoNombre(e.target.value)}
className="border rounded px-2 py-1 text-black"
/>
</td> </td>
<td className="py-2 px-4 border-b"> <td className="py-2 px-4 border-b">
<input value={nuevoCorreo} onChange={e => setNuevoCorreo(e.target.value)} className="border rounded px-2 py-1 text-black" /> <input
value={nuevoCorreo}
onChange={(e) => setNuevoCorreo(e.target.value)}
className="border rounded px-2 py-1 text-black"
/>
</td> </td>
<td className="py-2 px-4 border-b"> <td className="py-2 px-4 border-b">
<input value={nuevoNumero} onChange={e => setNuevoNumero(e.target.value)} className="border rounded px-2 py-1 text-black" /> <input
value={nuevoNumero}
onChange={(e) => setNuevoNumero(e.target.value)}
className="border rounded px-2 py-1 text-black"
/>
</td> </td>
<td className="py-2 px-4 border-b"> <td className="py-2 px-4 border-b">
<select <select
value={nuevoTipo} value={nuevoTipo}
onChange={e => { onChange={(e) => {
setNuevoTipo(e.target.value); setNuevoTipo(e.target.value);
setNuevaFormacion(""); setNuevaFormacion("");
}} }}
@ -222,12 +253,12 @@ export default function AlumnosVista() {
{nuevoTipo === "curso" && ( {nuevoTipo === "curso" && (
<select <select
value={nuevaFormacion} value={nuevaFormacion}
onChange={e => setNuevaFormacion(e.target.value)} onChange={(e) => setNuevaFormacion(e.target.value)}
className="border rounded px-2 py-1 text-black" className="border rounded px-2 py-1 text-black"
required required
> >
<option value="">Selecciona curso</option> <option value="">Selecciona curso</option>
{cursos.map(curso => ( {cursos.map((curso) => (
<option key={curso.id} value={curso.id}> <option key={curso.id} value={curso.id}>
{curso.nombre} {curso.nombre}
</option> </option>
@ -237,12 +268,12 @@ export default function AlumnosVista() {
{nuevoTipo === "inyeccion" && ( {nuevoTipo === "inyeccion" && (
<select <select
value={nuevaFormacion} value={nuevaFormacion}
onChange={e => setNuevaFormacion(e.target.value)} onChange={(e) => setNuevaFormacion(e.target.value)}
className="border rounded px-2 py-1 text-black" className="border rounded px-2 py-1 text-black"
required required
> >
<option value="">Selecciona inyección</option> <option value="">Selecciona inyección</option>
{inyecciones.map(inyeccion => ( {inyecciones.map((inyeccion) => (
<option key={inyeccion.id} value={inyeccion.id}> <option key={inyeccion.id} value={inyeccion.id}>
{inyeccion.nombre} {inyeccion.nombre}
</option> </option>
@ -252,12 +283,12 @@ export default function AlumnosVista() {
{nuevoTipo === "pildora" && ( {nuevoTipo === "pildora" && (
<select <select
value={nuevaFormacion} value={nuevaFormacion}
onChange={e => setNuevaFormacion(e.target.value)} onChange={(e) => setNuevaFormacion(e.target.value)}
className="border rounded px-2 py-1 text-black" className="border rounded px-2 py-1 text-black"
required required
> >
<option value="">Selecciona píldora</option> <option value="">Selecciona píldora</option>
{pildoras.map(pildora => ( {pildoras.map((pildora) => (
<option key={pildora.id} value={pildora.id}> <option key={pildora.id} value={pildora.id}>
{pildora.nombre} {pildora.nombre}
</option> </option>
@ -266,8 +297,18 @@ export default function AlumnosVista() {
)} )}
</td> </td>
<td className="py-2 px-4 border-b"> <td className="py-2 px-4 border-b">
<button onClick={() => guardarEdicion(alumno.id)} className="bg-green-500 hover:bg-green-700 text-white px-3 py-1 rounded mr-2">Guardar</button> <button
<button onClick={() => setAlumnoEditando(null)} className="bg-gray-400 hover:bg-gray-600 text-white px-3 py-1 rounded">Cancelar</button> onClick={() => guardarEdicion(alumno.id)}
className="bg-green-500 hover:bg-green-700 text-white px-3 py-1 rounded mr-2"
>
Guardar
</button>
<button
onClick={() => setAlumnoEditando(null)}
className="bg-gray-400 hover:bg-gray-600 text-white px-3 py-1 rounded"
>
Cancelar
</button>
</td> </td>
</tr> </tr>
) : ( ) : (
@ -286,9 +327,12 @@ export default function AlumnosVista() {
: ""} : ""}
</td> </td>
<td className="py-2 px-4 border-b"> <td className="py-2 px-4 border-b">
{alumno.tipo_formacion === "curso" && alumno.curso?.nombre} {alumno.tipo_formacion === "curso" &&
{alumno.tipo_formacion === "inyeccion" && alumno.inyeccion?.nombre} alumno.curso?.nombre}
{alumno.tipo_formacion === "pildora" && alumno.pildoras?.nombre} {alumno.tipo_formacion === "inyeccion" &&
alumno.inyeccion?.nombre}
{alumno.tipo_formacion === "pildora" &&
alumno.pildoras?.nombre}
</td> </td>
<td className="py-2 px-4 border-b"> <td className="py-2 px-4 border-b">
<button <button

View File

@ -12,6 +12,6 @@ export const alumnoSchema = z.object({
.regex(/^\d+$/, "El número de teléfono solo puede contener dígitos") .regex(/^\d+$/, "El número de teléfono solo puede contener dígitos")
.min(10, "El número de teléfono debe tener al menos 10 dígitos") .min(10, "El número de teléfono debe tener al menos 10 dígitos")
.max(10, "El número de teléfono no puede tener más de 10 dígitos"), .max(10, "El número de teléfono no puede tener más de 10 dígitos"),
//tipo: z.string().nonempty("Selecciona un tipo de asignación"), tipo: z.string().nonempty("Selecciona un tipo de formación"),
cursoSeleccionado: z.string().nonempty("Selecciona una opción"), cursoSeleccionado: z.string().nonempty("Selecciona una opción"),
}); });