feat: implement ID card generator with bulk upload, Excel parsing, Supabase and n8n integration

This commit is contained in:
2026-07-06 19:53:50 -04:00
parent 4b4685675c
commit f172d62ed4
5 changed files with 434 additions and 18 deletions
+17 -6
View File
@@ -65,11 +65,21 @@ export function convertDriveUrlToDirect(url) {
export async function fetchImageAsBlob(url) {
if (!url) return null;
if (url.includes('drive.google.com') || url.includes('docs.google.com')) {
if (url.startsWith('blob:') || url.startsWith('data:')) {
try {
const response = await fetch(url);
if (response.ok) return await response.blob();
} catch (e) {
console.error("Error al obtener Blob local de URL blob/data:", e);
}
return null;
}
if (url.includes('drive.google.com') || url.includes('docs.google.com')) {
try {
const response = await fetch(url);
if (response.ok) return await response.blob();
} catch {
console.warn("No se pudo obtener el Blob directo de Drive debido a restricciones CORS.");
}
return null;
@@ -85,7 +95,7 @@ export async function fetchImageAsBlob(url) {
try {
const response = await fetch(url, { mode: 'cors' });
return await response.blob();
} catch (err) {
} catch {
return null;
}
}
@@ -303,8 +313,9 @@ export async function downloadBulkIDCards({ employees, baseUrl, onStateChange, o
for (let i = 0; i < employees.length; i++) {
const emp = employees[i];
const currentClient = emp.selectedClient || 'Generico';
const cleanName = emp.name.trim().replace(/\s+/g, '') || `Empleado_${i + 1}`;
const folderName = `${cleanName}_${currentClient}`;
const sanitizedEmpName = (emp.name || '').trim().replace(/[\\/:*?"<>|]/g, '-') || `Empleado_${i + 1}`;
const sanitizedClientName = String(currentClient).trim().replace(/[\\/:*?"<>|]/g, '-');
const folderName = `${sanitizedEmpName} - ${sanitizedClientName}`;
const strictCleanId = String(emp.employeeId || '').replace(/[-\s]/g, '');
@@ -393,11 +404,11 @@ export async function downloadBulkIDCards({ employees, baseUrl, onStateChange, o
// Capturas gráficas del ZIP
const frontCanvas = await html2canvasLib(vFront, renderOpts);
const frontData = frontCanvas.toDataURL('image/png').split(',')[1];
zip.file(`${folderName}/${cleanName}_${currentClient}_Frente.png`, frontData, { base64: true });
zip.file(`${folderName}/Frente.png`, frontData, { base64: true });
const backCanvas = await html2canvasLib(vBack, renderOpts);
const backData = backCanvas.toDataURL('image/png').split(',')[1];
zip.file(`${folderName}/${cleanName}_${currentClient}_Reverso.png`, backData, { base64: true });
zip.file(`${folderName}/Reverso.png`, backData, { base64: true });
if (validatedPhotoUrl && validatedPhotoUrl.startsWith('blob:')) {
URL.revokeObjectURL(validatedPhotoUrl);