genración del pdf de plan de estudios ya funciona

This commit is contained in:
2025-12-04 14:01:09 -06:00
parent 9a1d8279a1
commit 1475a65938
3 changed files with 147 additions and 40 deletions

25
package-lock.json generated
View File

@@ -30,6 +30,7 @@
"@tanstack/router-plugin": "^1.121.2",
"@types/canvas-confetti": "^1.9.0",
"canvas-confetti": "^1.9.3",
"carbone-sdk-js": "^1.2.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
@@ -2819,6 +2820,12 @@
"node": ">=10.0.0"
}
},
"node_modules/carbone-sdk-js": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/carbone-sdk-js/-/carbone-sdk-js-1.2.2.tgz",
"integrity": "sha512-3bc4F04DizC8ULB6j6JsOq8G0sW/pwdwvfbbZ8exBZbcOCV4WE8KHsY6GiED/3tmH++Z1I5XMppjkBhjg60zjA==",
"license": "Apache-2.0"
},
"node_modules/ccount": {
"version": "2.0.1",
"license": "MIT",
@@ -3372,6 +3379,20 @@
"node": ">=8"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"license": "MIT",
@@ -5514,7 +5535,9 @@
}
},
"node_modules/vite": {
"version": "6.3.5",
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"license": "MIT",
"peer": true,
"dependencies": {

View File

@@ -34,6 +34,7 @@
"@tanstack/router-plugin": "^1.121.2",
"@types/canvas-confetti": "^1.9.0",
"canvas-confetti": "^1.9.3",
"carbone-sdk-js": "^1.2.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",

View File

@@ -1,49 +1,132 @@
import { Button } from "../ui/button"
import { Download } from "lucide-react"
import { supabase } from "@/auth/supabase";
import { Button } from "../ui/button";
import { Download } from "lucide-react";
export type PlanLike = Record<string, string | number | object | null | undefined>
export type PlanLike = Record<
string,
string | number | object | null | undefined
>;
export function DownloadPlanPDF({ plan }: { plan: PlanLike }) {
export function DownloadPlanPDF({ plan }: { plan: Record<string, any> }) {
async function fetchPDF() {
const planObj = {
...plan,
nivel_y_nombre_del_plan_de_estudios: `${plan["nivel"]} en ${plan["nombre"]}`,
nivel: undefined,
nombre: undefined,
};
const fileName = `Plan_${planObj.nivel_y_nombre_del_plan_de_estudios || "Desconocido"}.pdf`;
// const jsonData = JSON.stringify(planObj);
const triggerDownload = (blob: Blob, name: string) => {
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.setAttribute("download", name);
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
};
const fetchBinaryFallback = async () => {
// Intenta construir la URL del runtime de Functions
const anyClient = supabase as any;
const baseUrl =
anyClient?.functions?.url ||
`${(anyClient?.supabaseUrl || "").replace(/\/$/, "")}/functions/v1`;
const { data: sess } = await supabase.auth.getSession();
const token = sess?.session?.access_token;
console.log(JSON.stringify(planObj, null, 2));
console.log(planObj);
const resp = await fetch(`${baseUrl}/carbone-io-api`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/pdf",
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
body: JSON.stringify({
action: "downloadReport",
templateId: "1302213091201757023",
fileName,
convertTo: "pdf",
data: planObj,
}),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const blob = await resp.blob();
triggerDownload(blob, fileName);
};
try {
const response = await fetch(
"https://reportes-template.nicesand-99c6cbb8.westus2.azurecontainerapps.io/api/report",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/pdf",
},
body: JSON.stringify({
template: { name: "plan-estudios" },
data: {
nombre_autorizado_de_la_institucion: "Probando pdf",
// (plan["nombre"] ? String(plan["nombre"]) : "Plan de estudios"),
},
}),
}
)
// const { data, error } = await supabase.functions.invoke(
// "carbone-io-api",
// {
// method: "POST",
// headers: { Accept: "application/octet-stream" }, // preferir binario
// body: {
// action: "downloadReport",
// templateId: "1302213091201757023",
// fileName,
// convertTo: "pdf",
// data: planObj,
// },
// }
// );
if (!response.ok) {
const text = await response.text().catch(() => "")
console.error("Error HTTP al obtener PDF:", response.status, text)
throw new Error(`HTTP ${response.status}`)
}
// if (error) throw error;
const ct = response.headers.get("Content-Type") || ""
if (!ct.includes("application/pdf")) {
const text = await response.text().catch(() => "")
console.error("Respuesta no-PDF recibida:", text)
}
// // Si ya viene binario, descargar directo
// if (typeof Blob !== "undefined" && data instanceof Blob) {
// triggerDownload(data, fileName);
// return;
// }
// if (data instanceof ArrayBuffer) {
// triggerDownload(
// new Blob([data], { type: "application/pdf" }),
// fileName
// );
// return;
// }
const blob = await response.blob()
const pdfUrl = URL.createObjectURL(blob)
window.open(pdfUrl, "_blank")
// // Si vino como string (ej. empieza con %PDF), usa el fallback binario
// if (typeof data === "string") {
// await fetchBinaryFallback();
// return;
// }
// Limpia el objeto URL después de un tiempo
setTimeout(() => URL.revokeObjectURL(pdfUrl), 60_000)
// // Si vino JSON con base64, decodificar y descargar
// if (data && typeof data === "object") {
// const b64 =
// (data as any).file || (data as any).buffer || (data as any).base64;
// if (typeof b64 === "string") {
// const clean = b64.replace(/^data:.*;base64,/, "");
// const binary = atob(clean);
// const bytes = new Uint8Array(binary.length);
// for (let i = 0; i < binary.length; i++)
// bytes[i] = binary.charCodeAt(i);
// triggerDownload(
// new Blob([bytes], { type: "application/pdf" }),
// fileName
// );
// return;
// }
// }
// console.warn("Respuesta no reconocida para descarga de PDF.", {
// type: typeof data,
// });
await fetchBinaryFallback();
return;
} catch (error) {
console.error("Error al obtener PDF:", error)
console.error("Error al obtener PDF:", error);
}
}
@@ -56,7 +139,7 @@ export function DownloadPlanPDF({ plan }: { plan: PlanLike }) {
Descargar PDF
<Download className="w-4 h-4" />
</Button>
)
);
}
export default DownloadPlanPDF
export default DownloadPlanPDF;