feat: add loading and warning dialogs in cursosArchivo, enhance user feedback during course registration
This commit is contained in:
parent
9510e9a1a1
commit
6a4a44bdbc
|
@ -3,21 +3,45 @@ import Papa from "papaparse";
|
||||||
import * as XLSX from "xlsx";
|
import * as XLSX from "xlsx";
|
||||||
import Layout from "@/components/layout/Layout";
|
import Layout from "@/components/layout/Layout";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
|
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
export default function cursosArchivo() {
|
export default function cursosArchivo() {
|
||||||
const [archivo, setArchivo] = useState(null);
|
const [archivo, setArchivo] = useState(null);
|
||||||
const [datos, setDatos] = useState([]);
|
const [datos, setDatos] = useState([]);
|
||||||
const [dialogoAbierto, setDialogoAbierto] = useState(false);
|
const [dialogoAbierto, setDialogoAbierto] = useState(false);
|
||||||
const [mensajeDialogo, setMensajeDialogo] = useState("");
|
const [mensajeDialogo, setMensajeDialogo] = useState("");
|
||||||
|
const [dialogoCargando, setDialogoCargando] = useState(false);
|
||||||
|
const [dialogoAdvertencia, setDialogoAdvertencia] = useState(false);
|
||||||
|
const [rutaPendiente, setRutaPendiente] = useState(null);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (archivo) extraerContenido();
|
if (archivo) extraerContenido();
|
||||||
}, [archivo]);
|
}, [archivo]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleRouteChange = (url) => {
|
||||||
|
if (archivo && datos.length > 0) {
|
||||||
|
setDialogoAdvertencia(true);
|
||||||
|
setRutaPendiente(url);
|
||||||
|
// Cancelar navegación
|
||||||
|
throw "Bloqueo de navegación por archivo pendiente";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
router.events.on("routeChangeStart", handleRouteChange);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
router.events.off("routeChangeStart", handleRouteChange);
|
||||||
|
};
|
||||||
|
}, [archivo, datos, router]);
|
||||||
|
|
||||||
const registrarCursos = async () => {
|
const registrarCursos = async () => {
|
||||||
if (datos.length === 0) return;
|
if (datos.length === 0) return;
|
||||||
|
|
||||||
|
setDialogoCargando(true); // Mostrar dialogo de carga
|
||||||
|
|
||||||
const errores = [];
|
const errores = [];
|
||||||
|
|
||||||
for (const curso of datos) {
|
for (const curso of datos) {
|
||||||
|
@ -40,6 +64,8 @@ export default function cursosArchivo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDialogoCargando(false); // Ocultar dialogo de carga
|
||||||
|
|
||||||
if (errores.length > 0) {
|
if (errores.length > 0) {
|
||||||
setMensajeDialogo(`Se registraron algunos errores:\n${JSON.stringify(errores, null, 2)}`);
|
setMensajeDialogo(`Se registraron algunos errores:\n${JSON.stringify(errores, null, 2)}`);
|
||||||
} else {
|
} else {
|
||||||
|
@ -173,6 +199,47 @@ export default function cursosArchivo() {
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog open={dialogoCargando}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-black">Cargando...</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Por favor espera, se están registrando los cursos.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog open={dialogoAdvertencia} onOpenChange={setDialogoAdvertencia}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-black">Advertencia</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Si cambias de ventana perderás la subida del archivo. ¿Deseas continuar?
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
className="bg-red-500 hover:bg-red-700 text-white"
|
||||||
|
onClick={() => {
|
||||||
|
setDialogoAdvertencia(false);
|
||||||
|
setArchivo(null);
|
||||||
|
setDatos([]);
|
||||||
|
if (rutaPendiente) router.push(rutaPendiente);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sí, continuar
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="bg-gray-400 hover:bg-gray-600 text-white"
|
||||||
|
onClick={() => setDialogoAdvertencia(false)}
|
||||||
|
>
|
||||||
|
Cancelar
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -7,11 +7,21 @@ import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { supabaseClient } from "@/utils/supabase"; // Importar el cliente de Supabase
|
import { supabaseClient } from "@/utils/supabase"; // Importar el cliente de Supabase
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
|
||||||
export default function CursosManual() {
|
export default function CursosManual() {
|
||||||
const [addCompetencia, setAddCompetencia] = useState(false);
|
const [addCompetencia, setAddCompetencia] = useState(false);
|
||||||
const [competenciasGuardadas, setCompetenciasGuardadas] = useState([]);
|
const [competenciasGuardadas, setCompetenciasGuardadas] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [mostrarDialog, setMostrarDialog] = useState(false);
|
||||||
|
const [mensajeDialog, setMensajeDialog] = useState("");
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
resolver: zodResolver(cursosSchema),
|
resolver: zodResolver(cursosSchema),
|
||||||
|
@ -79,7 +89,8 @@ export default function CursosManual() {
|
||||||
console.error("Error al guardar en Supabase:", error.message);
|
console.error("Error al guardar en Supabase:", error.message);
|
||||||
alert("Error al guardar el curso: " + error.message);
|
alert("Error al guardar el curso: " + error.message);
|
||||||
} else {
|
} else {
|
||||||
alert("Curso guardado exitosamente");
|
setMensajeDialog("Curso guardado exitosamente");
|
||||||
|
setMostrarDialog(true);
|
||||||
form.reset(); // Reiniciar el formulario
|
form.reset(); // Reiniciar el formulario
|
||||||
setCompetenciasGuardadas([]); // Limpiar competencias guardadas
|
setCompetenciasGuardadas([]); // Limpiar competencias guardadas
|
||||||
}
|
}
|
||||||
|
@ -107,6 +118,11 @@ export function CursosManualForm({ nombreSugerido = "" }) {
|
||||||
const [addCompetencia, setAddCompetencia] = useState(false);
|
const [addCompetencia, setAddCompetencia] = useState(false);
|
||||||
const [competenciasGuardadas, setCompetenciasGuardadas] = useState([]); // [{id, descripcion}]
|
const [competenciasGuardadas, setCompetenciasGuardadas] = useState([]); // [{id, descripcion}]
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [mostrarDialog, setMostrarDialog] = useState(false);
|
||||||
|
const [mensajeDialog, setMensajeDialog] = useState("");
|
||||||
|
|
||||||
|
// Estado para dialog de competencia agregada
|
||||||
|
const [mostrarDialogCompetencia, setMostrarDialogCompetencia] = useState(false);
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
resolver: zodResolver(cursosSchema),
|
resolver: zodResolver(cursosSchema),
|
||||||
|
@ -126,14 +142,14 @@ export function CursosManualForm({ nombreSugerido = "" }) {
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = form;
|
} = form;
|
||||||
|
|
||||||
// Cambia para insertar la competencia en la tabla competencia
|
// Cambia handleSaveCompetencia para mostrar el dialog
|
||||||
const handleSaveCompetencia = async (e) => {
|
const handleSaveCompetencia = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const nuevaCompetencia = getValues("nuevaCompetencia").trim();
|
const nuevaCompetencia = getValues("nuevaCompetencia").trim();
|
||||||
if (!nuevaCompetencia) return;
|
if (!nuevaCompetencia) return;
|
||||||
|
|
||||||
// Verifica si ya existe en el estado
|
// Verifica si ya existe en el estado
|
||||||
if (competenciasGuardadas.some(c => c.descripcion === nuevaCompetencia)) {
|
if (competenciasGuardadas.some((c) => c.descripcion === nuevaCompetencia)) {
|
||||||
alert("La competencia ya fue agregada.");
|
alert("La competencia ya fue agregada.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -167,6 +183,7 @@ export function CursosManualForm({ nombreSugerido = "" }) {
|
||||||
]);
|
]);
|
||||||
setAddCompetencia(false);
|
setAddCompetencia(false);
|
||||||
setValue("nuevaCompetencia", "");
|
setValue("nuevaCompetencia", "");
|
||||||
|
setMostrarDialogCompetencia(true); // Mostrar dialog de éxito
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert("Error al guardar la competencia: " + (err.message || err));
|
alert("Error al guardar la competencia: " + (err.message || err));
|
||||||
}
|
}
|
||||||
|
@ -194,7 +211,8 @@ export function CursosManualForm({ nombreSugerido = "" }) {
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (errorCurso) {
|
if (errorCurso) {
|
||||||
alert("Error al guardar el curso: " + errorCurso.message);
|
setMensajeDialog("Error al guardar el curso: " + errorCurso.message);
|
||||||
|
setMostrarDialog(true);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -211,13 +229,15 @@ export function CursosManualForm({ nombreSugerido = "" }) {
|
||||||
.from("curso_competencia")
|
.from("curso_competencia")
|
||||||
.insert(relaciones);
|
.insert(relaciones);
|
||||||
if (errorPivote) {
|
if (errorPivote) {
|
||||||
alert("Error al asociar competencias: " + errorPivote.message);
|
setMensajeDialog("Error al asociar competencias: " + errorPivote.message);
|
||||||
|
setMostrarDialog(true);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
alert("Curso guardado exitosamente");
|
setMensajeDialog("Curso guardado exitosamente");
|
||||||
|
setMostrarDialog(true);
|
||||||
form.reset();
|
form.reset();
|
||||||
setCompetenciasGuardadas([]);
|
setCompetenciasGuardadas([]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -259,6 +279,9 @@ export function CursosManualForm({ nombreSugerido = "" }) {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<h2 className="text-lg font-semibold mb-3 text-black">Competencias</h2>
|
<h2 className="text-lg font-semibold mb-3 text-black">Competencias</h2>
|
||||||
|
<p className="text-xs text-gray-500 mb-2">
|
||||||
|
Puedes agregar competencias nuevas sin necesidad de crear un nuevo curso. Las competencias se guardarán y podrás asociarlas a otros cursos después.
|
||||||
|
</p>
|
||||||
|
|
||||||
{competenciasGuardadas.length > 0 && (
|
{competenciasGuardadas.length > 0 && (
|
||||||
<div className="mt-5 w-full flex-wrap">
|
<div className="mt-5 w-full flex-wrap">
|
||||||
|
@ -334,6 +357,32 @@ export function CursosManualForm({ nombreSugerido = "" }) {
|
||||||
{loading ? "Guardando..." : "Guardar curso"}
|
{loading ? "Guardando..." : "Guardar curso"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Dialog open={mostrarDialog} onOpenChange={setMostrarDialog}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-black">Resultado</DialogTitle>
|
||||||
|
<DialogDescription>{mensajeDialog}</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button onClick={() => setMostrarDialog(false)}>Cerrar</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog open={mostrarDialogCompetencia} onOpenChange={setMostrarDialogCompetencia}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-black">Competencia agregada</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
¡La competencia fue agregada exitosamente!
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button onClick={() => setMostrarDialogCompetencia(false)}>Cerrar</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue