commit wip

This commit is contained in:
2025-10-22 15:54:42 -06:00
parent d491100c73
commit 3fccdc0478
6 changed files with 46 additions and 9 deletions

View File

@@ -43,7 +43,7 @@ export function AddAsignaturaButton({ planId, onAdded }: { planId: string; onAdd
horas_teoricas: toNum(f.horas_teoricas), horas_teoricas: toNum(f.horas_teoricas),
horas_practicas: toNum(f.horas_practicas), horas_practicas: toNum(f.horas_practicas),
objetivos: toNull(f.objetivos), objetivos: toNull(f.objetivos),
contenidos: {}, bibliografia: [], criterios_evaluacion: null, contenidos: [], bibliografia: [], criterios_evaluacion: null,
} }
const { error } = await supabase.from("asignaturas").insert([payload]) const { error } = await supabase.from("asignaturas").insert([payload])
setSaving(false) setSaving(false)

View File

@@ -28,10 +28,10 @@ export function DeletePlanButton({ planId, onDeleted }: { planId: string; onDele
return confirm ? ( return confirm ? (
<div className="flex gap-2"> <div className="flex gap-2">
<Button variant="outline" onClick={() => setConfirm(false)} disabled={loading}>Cancelar</Button>
<Button variant="destructive" onClick={handleDelete} disabled={loading}> <Button variant="destructive" onClick={handleDelete} disabled={loading}>
{loading ? "Eliminando…" : "Confirmar eliminación"} {loading ? "Eliminando…" : "Confirmar eliminación"}
</Button> </Button>
<Button variant="outline" onClick={() => setConfirm(false)} disabled={loading}>Cancelar</Button>
</div> </div>
) : ( ) : (
<Button variant="outline" onClick={() => setConfirm(true)}> <Button variant="outline" onClick={() => setConfirm(true)}>

View File

@@ -0,0 +1,33 @@
import { Download } from "lucide-react";
import { Button } from "../ui/button";
export function DescargarPdfButton({planId, opcion}: {planId: string, opcion: "plan" | "asignaturas"}) {
return (
<Button variant="outline" className="flex items-center gap-2 " onClick={() => descargarPdf(planId, opcion)}>
Descargar {opcion === "plan" ? "Plan" : "Asignaturas"} PDF
<Download className="w-4 h-4" />
</Button>
)
}
function descargarPdf(planId: string, opcion: "plan" | "asignaturas") {
// Lógica para generar y descargar el PDF del plan de estudios
try {
// Usa la variable de entorno para construir la URL completa
const pdfUrl = opcion === "plan"
? `${import.meta.env.VITE_BACK_ORIGIN}/api/planes/${planId}/descargar-pdf-plan`
: `${import.meta.env.VITE_BACK_ORIGIN}/api/planes/${planId}/descargar-pdf-asignaturas`;
const link = document.createElement('a');
link.href = pdfUrl;
link.download = opcion === "plan"
? `plan_estudios_${planId}.pdf`
: `asignaturas_plan_estudios_${planId}.pdf`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (error) {
console.error("Error al descargar el PDF:", error);
alert("Hubo un error al descargar el PDF. Por favor, inténtalo de nuevo.");
}
}

View File

@@ -135,7 +135,7 @@ function Page() {
{/* ===== Hero ===== */} {/* ===== Hero ===== */}
<div className="relative overflow-hidden rounded-3xl border shadow-sm"> <div className="relative overflow-hidden rounded-3xl border shadow-sm">
<div className={`absolute inset-0 bg-gradient-to-br ${style.halo} via-white to-transparent`} /> <div className={`absolute inset-0 bg-gradient-to-br ${style.halo} via-white to-transparent`} />
<div className="relative p-6 flex flex-col gap-4 md:flex-row md:items-center md:justify-between"> <div className="relative p-6 flex flex-col grid grid-cols-1 gap-4 md:flex-row md:items-center md:justify-between">
<div className="min-w-0"> <div className="min-w-0">
<div className="inline-flex items-center gap-2 text-xs text-neutral-600"> <div className="inline-flex items-center gap-2 text-xs text-neutral-600">
<Icons.BookOpen className="h-4 w-4" /> Asignatura <Icons.BookOpen className="h-4 w-4" /> Asignatura
@@ -613,10 +613,10 @@ function BorrarAsignaturaButton({ asignatura_id, onDeleted }: { asignatura_id: s
return confirm ? ( return confirm ? (
<div className="flex gap-2"> <div className="flex gap-2">
<Button variant="outline" onClick={() => setConfirm(false)} disabled={loading}>Cancelar</Button>
<Button variant="destructive" onClick={handleDelete} disabled={loading}> <Button variant="destructive" onClick={handleDelete} disabled={loading}>
{loading ? "Eliminando…" : "Confirmar eliminación"} {loading ? "Eliminando…" : "Confirmar eliminación"}
</Button> </Button>
<Button variant="outline" onClick={() => setConfirm(false)} disabled={loading}>Cancelar</Button>
</div> </div>
) : ( ) : (
<Button variant="outline" onClick={() => setConfirm(true)}> <Button variant="outline" onClick={() => setConfirm(true)}>

View File

@@ -20,6 +20,7 @@ import { AuroraButton } from "@/components/effect/aurora-button"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { DeletePlanButton } from "@/components/planes/DeletePlan" import { DeletePlanButton } from "@/components/planes/DeletePlan"
import { AddAsignaturaButton } from "@/components/planes/AddAsignaturaButton" import { AddAsignaturaButton } from "@/components/planes/AddAsignaturaButton"
import { DescargarPdfButton } from "@/components/planes/GenerarPdfButton"
type LoaderData = { plan: PlanFull; asignaturas: AsignaturaLite[] } type LoaderData = { plan: PlanFull; asignaturas: AsignaturaLite[] }
@@ -79,7 +80,7 @@ function RouteComponent() {
</nav> </nav>
<Card ref={headerRef} className="relative overflow-hidden border shadow-sm"> <Card ref={headerRef} className="relative overflow-hidden border shadow-sm">
<div className="absolute inset-0 -z-0" style={accent} /> <div className="absolute inset-0 -z-0" style={accent} />
<CardHeader className="relative z-10 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"> <CardHeader className="relative z-10 flex flex-col grid grid-cols-1 gap-3 sm:flex-row sm:items-center sm:justify-between">
<div className="flex items-center gap-3 min-w-0"> <div className="flex items-center gap-3 min-w-0">
<span className="hdr-icon inline-flex items-center justify-center rounded-2xl border px-3 py-2 bg-white/70" <span className="hdr-icon inline-flex items-center justify-center rounded-2xl border px-3 py-2 bg-white/70"
style={{ borderColor: accent.borderColor as string }}> style={{ borderColor: accent.borderColor as string }}>
@@ -99,11 +100,13 @@ function RouteComponent() {
{plan.estado} {plan.estado}
</Badge> </Badge>
)} )}
<div className='flex gap-2'> {/* <div className='flex gap-2'> */}
<EditPlanButton plan={plan} /> <EditPlanButton plan={plan} />
<AdjustAIButton plan={plan} /> <AdjustAIButton plan={plan} />
<DescargarPdfButton planId={plan.id} opcion="plan" />
<DescargarPdfButton planId={plan.id} opcion="asignaturas" />
<DeletePlanButton planId={plan.id} /> <DeletePlanButton planId={plan.id} />
</div> {/* </div> */}
</div> </div>
</CardHeader> </CardHeader>
<CardContent ref={statsRef}> <CardContent ref={statsRef}>

View File

@@ -1,4 +1,4 @@
import { createFileRoute, redirect } from "@tanstack/react-router" import { createFileRoute, redirect, useRouter } from "@tanstack/react-router"
import { useState } from "react" import { useState } from "react"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
@@ -27,6 +27,7 @@ function LoginComponent() {
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState("") const [error, setError] = useState("")
const [showPassword, setShowPassword] = useState(false) const [showPassword, setShowPassword] = useState(false)
const router = useRouter()
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault() e.preventDefault()
@@ -35,7 +36,7 @@ function LoginComponent() {
try { try {
await auth.login(email, password) await auth.login(email, password)
window.location.href = redirect router.navigate({ to: redirect})
} catch (err: any) { } catch (err: any) {
setError(err.message || "No fue posible iniciar sesión") setError(err.message || "No fue posible iniciar sesión")
} finally { } finally {