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", "@tanstack/router-plugin": "^1.121.2",
"@types/canvas-confetti": "^1.9.0", "@types/canvas-confetti": "^1.9.0",
"canvas-confetti": "^1.9.3", "canvas-confetti": "^1.9.3",
"carbone-sdk-js": "^1.2.2",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.1.1", "cmdk": "^1.1.1",
@@ -2819,6 +2820,12 @@
"node": ">=10.0.0" "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": { "node_modules/ccount": {
"version": "2.0.1", "version": "2.0.1",
"license": "MIT", "license": "MIT",
@@ -3372,6 +3379,20 @@
"node": ">=8" "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": { "node_modules/gensync": {
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"license": "MIT", "license": "MIT",
@@ -5514,7 +5535,9 @@
} }
}, },
"node_modules/vite": { "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", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {

View File

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

View File

@@ -1,49 +1,132 @@
import { Button } from "../ui/button" import { supabase } from "@/auth/supabase";
import { Download } from "lucide-react" 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() { 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 { try {
const response = await fetch( // const { data, error } = await supabase.functions.invoke(
"https://reportes-template.nicesand-99c6cbb8.westus2.azurecontainerapps.io/api/report", // "carbone-io-api",
{ // {
method: "POST", // method: "POST",
headers: { // headers: { Accept: "application/octet-stream" }, // preferir binario
"Content-Type": "application/json", // body: {
Accept: "application/pdf", // action: "downloadReport",
}, // templateId: "1302213091201757023",
body: JSON.stringify({ // fileName,
template: { name: "plan-estudios" }, // convertTo: "pdf",
data: { // data: planObj,
nombre_autorizado_de_la_institucion: "Probando pdf", // },
// (plan["nombre"] ? String(plan["nombre"]) : "Plan de estudios"), // }
}, // );
}),
}
)
if (!response.ok) { // if (error) throw error;
const text = await response.text().catch(() => "")
console.error("Error HTTP al obtener PDF:", response.status, text)
throw new Error(`HTTP ${response.status}`)
}
const ct = response.headers.get("Content-Type") || "" // // Si ya viene binario, descargar directo
if (!ct.includes("application/pdf")) { // if (typeof Blob !== "undefined" && data instanceof Blob) {
const text = await response.text().catch(() => "") // triggerDownload(data, fileName);
console.error("Respuesta no-PDF recibida:", text) // return;
} // }
// if (data instanceof ArrayBuffer) {
// triggerDownload(
// new Blob([data], { type: "application/pdf" }),
// fileName
// );
// return;
// }
const blob = await response.blob() // // Si vino como string (ej. empieza con %PDF), usa el fallback binario
const pdfUrl = URL.createObjectURL(blob) // if (typeof data === "string") {
window.open(pdfUrl, "_blank") // await fetchBinaryFallback();
// return;
// }
// Limpia el objeto URL después de un tiempo // // Si vino JSON con base64, decodificar y descargar
setTimeout(() => URL.revokeObjectURL(pdfUrl), 60_000) // 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) { } 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 Descargar PDF
<Download className="w-4 h-4" /> <Download className="w-4 h-4" />
</Button> </Button>
) );
} }
export default DownloadPlanPDF export default DownloadPlanPDF;