Se crea funcionalidad de exportar pdf desde front y generar historial de version de cambios se agrego una libreri jspdf

This commit is contained in:
2025-11-05 15:19:38 -06:00
parent daac6f3f6d
commit 9462e25a20
8 changed files with 6622 additions and 91 deletions

View File

@@ -20,6 +20,7 @@ import { AuroraButton } from "@/components/effect/aurora-button"
import { DeletePlanButton } from "@/components/planes/DeletePlan"
import { AddAsignaturaButton } from "@/components/planes/AddAsignaturaButton"
import { DescargarPdfButton } from "@/components/planes/GenerarPdfButton"
import { DownloadPlanPDF } from "@/components/planes/DownloadPlanPDF"
type LoaderData = { plan: PlanFull; asignaturas: AsignaturaLite[] }
@@ -105,7 +106,8 @@ function RouteComponent() {
{/* <div className='flex gap-2'> */}
<EditPlanButton plan={plan} />
<AdjustAIButton plan={plan} />
<DescargarPdfButton planId={plan.id} opcion="plan" />
{/* <DescargarPdfButton planId={plan.id} opcion="plan" /> */}
<DownloadPlanPDF plan={plan} />
<DescargarPdfButton planId={plan.id} opcion="asignaturas" />
<DeletePlanButton planId={plan.id} />
{/* </div> */}
@@ -203,33 +205,77 @@ function StatCard({ label, value = "—", Icon = Icons.Info, accent, className =
/* ===== Editar ===== */
function EditPlanButton({ plan }: { plan: PlanFull }) {
const auth = useSupabaseAuth()
const [open, setOpen] = useState(false)
const [form, setForm] = useState<Partial<PlanFull>>({})
const [saving, setSaving] = useState(false)
const qc = useQueryClient()
// Función para comparar valores y generar diffs tipo JSON Patch
function generateDiff(oldData: PlanFull, newData: Partial<PlanFull>) {
const changes: any[] = []
for (const key of Object.keys(newData)) {
const oldValue = (oldData as any)[key]
const newValue = (newData as any)[key]
if (newValue !== undefined && newValue !== oldValue) {
changes.push({
op: "replace",
path: `/${key}`,
from: oldValue,
value: newValue
})
}
}
return changes
}
const mutation = useMutation({
mutationFn: async (payload: Partial<PlanFull>) => {
const { error } = await supabase.from('plan_estudios').update({
nombre: payload.nombre ?? plan.nombre,
nivel: payload.nivel ?? plan.nivel,
duracion: payload.duracion ?? plan.duracion,
total_creditos: payload.total_creditos ?? plan.total_creditos,
}).eq('id', plan.id)
// 1⃣ Generar las diferencias antes del update
const diff = generateDiff(plan, payload)
// 2⃣ Guardar respaldo (solo si hay cambios)
if (diff.length > 0) {
const { error: backupError } = await supabase.from("historico_cambios").insert({
id_plan_estudios: plan.id,
json_cambios: diff, // jsonb
user_id:auth.user?.id,
created_at: new Date().toISOString()
})
if (backupError) throw backupError
}
// 3⃣ Actualizar el plan principal
const { error } = await supabase
.from("plan_estudios")
.update({
nombre: payload.nombre ?? plan.nombre,
nivel: payload.nivel ?? plan.nivel,
duracion: payload.duracion ?? plan.duracion,
total_creditos: payload.total_creditos ?? plan.total_creditos,
})
.eq("id", plan.id)
if (error) throw error
},
onMutate: async (payload) => {
await qc.cancelQueries({ queryKey: planKeys.byId(plan.id) })
const prev = qc.getQueryData<PlanFull>(planKeys.byId(plan.id))
qc.setQueryData<PlanFull>(planKeys.byId(plan.id), (old) => old ? { ...old, ...payload } as PlanFull : old as any)
qc.setQueryData<PlanFull>(
planKeys.byId(plan.id),
(old) => (old ? { ...old, ...payload } as PlanFull : old as any)
)
return { prev }
},
onError: (_e, _vars, ctx) => {
if (ctx?.prev) qc.setQueryData(planKeys.byId(plan.id), ctx.prev)
},
onSettled: async () => {
await qc.invalidateQueries({ queryKey: planKeys.byId(plan.id) })
}
},
})
async function save() {