feat: implement form validation for alumno editing using Zod schema
This commit is contained in:
parent
6c5a04aec4
commit
65893bf052
|
@ -19,14 +19,16 @@ import {
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { set } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { alumnoSchema } from "@/schemas/AlumnosSchema";
|
||||||
|
|
||||||
export default function AlumnosVista() {
|
export default function AlumnosVista() {
|
||||||
const [alumnos, setAlumnos] = useState([]);
|
const [alumnos, setAlumnos] = useState([]);
|
||||||
const [alumnoEditando, setAlumnoEditando] = useState(null);
|
const [alumnoEditando, setAlumnoEditando] = useState(null);
|
||||||
const [nuevoNombre, setNuevoNombre] = useState("");
|
const [nuevoNombre, setNuevoNombre] = useState("");
|
||||||
const [nuevoCorreo, setNuevoCorreo] = useState("");
|
const [nuevoCorreo, setNuevoCorreo] = useState("");
|
||||||
const [nuevoNumero, setNuevoNumero] = useState("");
|
const [nuevoNumero, setNuevoNumero] = useState("");
|
||||||
const [mostrarModal, setMostrarModal] = useState(false);
|
const [mostrarModal, setMostrarModal] = useState(false);
|
||||||
const [modalMensaje, setModalMensaje] = useState("");
|
const [modalMensaje, setModalMensaje] = useState("");
|
||||||
const [nuevoCurso, setNuevoCurso] = useState("");
|
const [nuevoCurso, setNuevoCurso] = useState("");
|
||||||
|
@ -62,38 +64,50 @@ export default function AlumnosVista() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
setValue,
|
||||||
|
reset,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm({
|
||||||
|
resolver: zodResolver(alumnoSchema),
|
||||||
|
defaultValues: {
|
||||||
|
nombre: "",
|
||||||
|
correo: "",
|
||||||
|
telefono: "",
|
||||||
|
cursoSeleccionado: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// Iniciar edición
|
// Iniciar edición
|
||||||
const iniciarEdicion = (alumno) => {
|
const iniciarEdicion = (alumno) => {
|
||||||
setAlumnoEditando(alumno.id);
|
setAlumnoEditando(alumno.id);
|
||||||
setNuevoNombre(alumno.nombre);
|
setValue("nombre", alumno.nombre);
|
||||||
setNuevoCorreo(alumno.correo);
|
setValue("correo", alumno.correo);
|
||||||
setNuevoNumero(alumno.telefono);
|
setValue("telefono", alumno.telefono);
|
||||||
setNuevoCurso(alumno.curso_id);
|
setValue("cursoSeleccionado", alumno.curso_id?.toString() || "");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cancelar edición
|
// Cancelar edición
|
||||||
const cancelarEdicion = () => {
|
const cancelarEdicion = () => {
|
||||||
setAlumnoEditando(null);
|
setAlumnoEditando(null);
|
||||||
setNuevoNombre("");
|
reset();
|
||||||
setNuevoCorreo("");
|
|
||||||
setNuevoNumero("");
|
|
||||||
setNuevoCurso("");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Guardar cambios
|
// Guardar cambios
|
||||||
const guardarEdicion = async (id) => {
|
const guardarEdicion = async (data) => {
|
||||||
const { error } = await supabaseClient
|
const { error } = await supabaseClient
|
||||||
.from("alumno")
|
.from("alumno")
|
||||||
.update({
|
.update({
|
||||||
nombre: nuevoNombre,
|
nombre: data.nombre,
|
||||||
correo: nuevoCorreo,
|
correo: data.correo,
|
||||||
telefono: nuevoNumero,
|
telefono: data.telefono,
|
||||||
curso_id: nuevoCurso,
|
curso_id: data.cursoSeleccionado,
|
||||||
})
|
})
|
||||||
.eq("id", id);
|
.eq("id", alumnoEditando);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error("Error actualizando alumno:", error.message);
|
|
||||||
setModalMensaje("Error al actualizar el alumno");
|
setModalMensaje("Error al actualizar el alumno");
|
||||||
} else {
|
} else {
|
||||||
setModalMensaje("Alumno actualizado exitosamente");
|
setModalMensaje("Alumno actualizado exitosamente");
|
||||||
|
@ -111,7 +125,10 @@ export default function AlumnosVista() {
|
||||||
|
|
||||||
// Eliminar alumno
|
// Eliminar alumno
|
||||||
const eliminarAlumno = async () => {
|
const eliminarAlumno = async () => {
|
||||||
const { error } = await supabaseClient.from("alumno").delete().eq("id", alumnoAEliminar);
|
const { error } = await supabaseClient
|
||||||
|
.from("alumno")
|
||||||
|
.delete()
|
||||||
|
.eq("id", alumnoAEliminar);
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error("Error eliminando alumno:", error.message);
|
console.error("Error eliminando alumno:", error.message);
|
||||||
setModalMensaje("Error al eliminar el alumno");
|
setModalMensaje("Error al eliminar el alumno");
|
||||||
|
@ -148,47 +165,61 @@ export default function AlumnosVista() {
|
||||||
{alumno.id}
|
{alumno.id}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 px-4 border-b">
|
<td className="py-2 px-4 border-b">
|
||||||
<Input
|
<Input type="text" {...register("nombre")} />
|
||||||
type="text"
|
{errors.nombre && (
|
||||||
value={nuevoNombre}
|
<span className="text-red-500 text-xs">
|
||||||
onChange={(e) => setNuevoNombre(e.target.value)}
|
{errors.nombre.message}
|
||||||
/>
|
</span>
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 px-4 border-b">
|
<td className="py-2 px-4 border-b">
|
||||||
<Input
|
<Input type="email" {...register("correo")} />
|
||||||
type="email"
|
{errors.correo && (
|
||||||
value={nuevoCorreo}
|
<span className="text-red-500 text-xs">
|
||||||
onChange={(e) => setNuevoCorreo(e.target.value)}
|
{errors.correo.message}
|
||||||
/>
|
</span>
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 px-4 border-b">
|
<td className="py-2 px-4 border-b">
|
||||||
<Input
|
<Input type="text" {...register("telefono")} />
|
||||||
type="text"
|
{errors.telefono && (
|
||||||
value={nuevoNumero}
|
<span className="text-red-500 text-xs">
|
||||||
onChange={(e) => setNuevoNumero(e.target.value)}
|
{errors.telefono.message}
|
||||||
/>
|
</span>
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 px-4 border-b">
|
<td className="py-2 px-4 border-b">
|
||||||
<Select
|
<Select
|
||||||
value={nuevoCurso}
|
value={undefined}
|
||||||
onValueChange={(value) => setNuevoCurso(value)}
|
onValueChange={(value) =>
|
||||||
|
setValue("cursoSeleccionado", value)
|
||||||
|
}
|
||||||
|
{...register("cursoSeleccionado")}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Selecciona un curso" />
|
<SelectValue placeholder="Selecciona un curso" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{cursos.map((curso) => (
|
{cursos.map((curso) => (
|
||||||
<SelectItem key={curso.id} value={curso.id.toString()}>
|
<SelectItem
|
||||||
|
key={curso.id}
|
||||||
|
value={curso.id.toString()}
|
||||||
|
>
|
||||||
{curso.nombre}
|
{curso.nombre}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
{errors.cursoSeleccionado && (
|
||||||
|
<span className="text-red-500 text-xs">
|
||||||
|
{errors.cursoSeleccionado.message}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 px-4 border-b flex justify-center">
|
<td className="py-2 px-4 border-b flex justify-center">
|
||||||
<Button
|
<Button
|
||||||
className="bg-green-500 hover:bg-green-700 text-white font-bold py-1 px-3 m-2 rounded"
|
className="bg-green-500 hover:bg-green-700 text-white font-bold py-1 px-3 m-2 rounded"
|
||||||
onClick={() => guardarEdicion(alumno.id)}
|
onClick={handleSubmit(guardarEdicion)}
|
||||||
>
|
>
|
||||||
Guardar
|
Guardar
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -206,7 +237,9 @@ export default function AlumnosVista() {
|
||||||
<td className="py-2 px-4 border-b">{alumno.nombre}</td>
|
<td className="py-2 px-4 border-b">{alumno.nombre}</td>
|
||||||
<td className="py-2 px-4 border-b">{alumno.correo}</td>
|
<td className="py-2 px-4 border-b">{alumno.correo}</td>
|
||||||
<td className="py-2 px-4 border-b">{alumno.telefono}</td>
|
<td className="py-2 px-4 border-b">{alumno.telefono}</td>
|
||||||
<td className="py-2 px-4 border-b">{alumno.curso?.nombre || "Sin curso"}</td>
|
<td className="py-2 px-4 border-b">
|
||||||
|
{alumno.curso?.nombre || "Sin curso"}
|
||||||
|
</td>
|
||||||
<td className="py-2 px-4 border-b space-x-2">
|
<td className="py-2 px-4 border-b space-x-2">
|
||||||
<Button
|
<Button
|
||||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-3 rounded"
|
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-3 rounded"
|
||||||
|
@ -233,9 +266,12 @@ export default function AlumnosVista() {
|
||||||
<Dialog open={confirmarEliminar} onOpenChange={setConfirmarEliminar}>
|
<Dialog open={confirmarEliminar} onOpenChange={setConfirmarEliminar}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="text-black">Confirmar eliminación</DialogTitle>
|
<DialogTitle className="text-black">
|
||||||
|
Confirmar eliminación
|
||||||
|
</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
¿Estás seguro de que deseas eliminar este alumno? Esta acción no se puede deshacer.
|
¿Estás seguro de que deseas eliminar este alumno? Esta acción no
|
||||||
|
se puede deshacer.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
|
@ -271,4 +307,4 @@ export default function AlumnosVista() {
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ export const alumnoSchema = z.object({
|
||||||
telefono: z
|
telefono: z
|
||||||
.string()
|
.string()
|
||||||
.nonempty("Escribe el número de teléfono")
|
.nonempty("Escribe el número de teléfono")
|
||||||
.regex(/^\d+$/, "Solo se permiten números en el teléfono"),
|
.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")
|
||||||
|
.max(10, "El número de teléfono no puede tener más de 10 dígitos"),
|
||||||
cursoSeleccionado: z.string().nonempty("Selecciona un curso"),
|
cursoSeleccionado: z.string().nonempty("Selecciona un curso"),
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue