From dfa0d63061dc3f6823d2918858d1daffc877ce32 Mon Sep 17 00:00:00 2001 From: EidanThen Date: Tue, 7 Jul 2026 16:55:11 -0400 Subject: [PATCH] feat: implementacion de flujo n8n en el formulario principal --- src/Generator.css | 2 +- src/Generator.jsx | 86 ++++++++++++++++++++++++++++++++++++++----- src/script/script.jsx | 4 +- 3 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/Generator.css b/src/Generator.css index d99dd91..dc2125d 100644 --- a/src/Generator.css +++ b/src/Generator.css @@ -273,7 +273,7 @@ body { width: 100%; height: 100%; object-fit: cover; - display: none; + display: block; } .front-photo-frame .placeholder-svg { diff --git a/src/Generator.jsx b/src/Generator.jsx index 8fa73bf..59e805c 100644 --- a/src/Generator.jsx +++ b/src/Generator.jsx @@ -46,8 +46,12 @@ export default function IdCardGenerator() { const [employeeId, setEmployeeId] = useState(''); const [photoSrc, setPhotoSrc] = useState(''); const [photoFile, setPhotoFile] = useState(null); + const [photoProcessing, setPhotoProcessing] = useState(false); const [downloadStatus, setDownloadStatus] = useState({ processing: false, text: '⬇ Descargar' }); + // Bloquea el botón Descargar hasta que nombre, puesto e ID estén llenos + const isFormComplete = name.trim() && role.trim() && employeeId.trim(); + // --- Estados Lote / Excel Masivo ─── const [bulkEmployees, setBulkEmployees] = useState([]); const [bulkStatusText, setBulkStatusText] = useState(''); @@ -68,14 +72,65 @@ export default function IdCardGenerator() { } }, [employeeId]); - const handlePhotoUpload = (e) => { + const handlePhotoUpload = async (e) => { const file = e.target.files[0]; - if (file) { - setPhotoFile(file); - setPhotoSrc(URL.createObjectURL(file)); + if (!file) return; + + setPhotoFile(file); + setPhotoSrc(URL.createObjectURL(file)); + setPhotoProcessing(true); + + try { + const editedBlob = await processPhotoWithAI(file); + const editedUrl = URL.createObjectURL(editedBlob); + setPhotoSrc(editedUrl); + setPhotoFile(new File([editedBlob], file.name, { type: editedBlob.type || 'image/jpeg' })); + } catch (error) { + console.error('Error al procesar la foto con IA en n8n:', error); + alert('No se pudo procesar la foto con IA. Se usará la foto original para la vista previa.'); + } finally { + setPhotoProcessing(false); } }; + const processPhotoWithAI = async (file) => { + const N8N_WEBHOOK_URL = import.meta.env.VITE_N8N_WEBHOOK_URL; + const TOKEN_SECRETO = import.meta.env.VITE_WEBHOOK_TOKEN; + + const formData = new FormData(); + formData.append('edited', file, file.name); + formData.append('name', name); + formData.append('role', role); + formData.append('employeeId', employeeId); + formData.append('fechaProcesado', new Date().toISOString()); + + const response = await fetch(N8N_WEBHOOK_URL, { + method: 'POST', + headers: { + 'Authorization': TOKEN_SECRETO + }, + body: formData + }); + + if (!response.ok) { + throw new Error(`El servidor de n8n rechazó la petición (Código: ${response.status})`); + } + + const contentType = response.headers.get('content-type') || ''; + if (contentType.includes('application/json')) { + const data = await response.json(); + const bin = data.edited || data.data || data.foto_processed_base64 || data.photo || data.binary || (Array.isArray(data) && (data[0]?.edited || data[0]?.data)); + const blobUrl = convertBinaryToBlobUrl(bin); + if (!blobUrl) throw new Error('n8n no devolvió un binario válido.'); + const blob = await (await fetch(blobUrl)).blob(); + return blob; + } + + const blob = await response.blob(); + if (!blob || blob.size === 0) throw new Error('n8n devolvió un binario vacío.'); + return blob; + }; + const handleEmployeeIdChange = (e) => { const rawValue = e.target.value; const cleanValue = rawValue.replace(/[-\s]/g, ''); @@ -217,7 +272,7 @@ export default function IdCardGenerator() { if (bulkEmployees.length === 0) return; setBulkDownloadStatus({ processing: true, text: '🔄 Descargando...' }); - setBulkStatusText('Conectando con n8n y descargando retratos optimizados por IA...'); + setBulkStatusText('Descargando retratos...'); try { const resultadosN8n = await enviarDatosAn8n(bulkEmployees); @@ -253,7 +308,7 @@ export default function IdCardGenerator() { } }); - setBulkStatusText('🎉 ¡Lote de carnets guardado correctamente y registrado en Supabase!'); + setBulkStatusText('🎉 ¡Lote de carnets guardado correctamente!'); setBulkEmployees([]); } catch (error) { @@ -394,8 +449,8 @@ export default function IdCardGenerator() { setRole(e.target.value)} /> -
fileInputRef.current.click()}> - Subir foto del colaborador +
!photoProcessing && fileInputRef.current.click()} style={{ opacity: photoProcessing ? 0.6 : 1, cursor: photoProcessing ? 'wait' : 'pointer' }}> + {photoProcessing ? '⏳ Procesando foto con IA...' : 'Subir foto del colaborador'}
@@ -447,7 +502,7 @@ export default function IdCardGenerator() {
Exportar
-
@@ -461,7 +516,18 @@ export default function IdCardGenerator() {
{photoSrc ? ( Foto Colaborador - ) : ( + ) : (null)} + {photoProcessing && ( +
+ ⏳ Editando foto con IA... +
+ )} + {!photoSrc && ( diff --git a/src/script/script.jsx b/src/script/script.jsx index 112416a..1b85a01 100644 --- a/src/script/script.jsx +++ b/src/script/script.jsx @@ -221,7 +221,7 @@ export async function downloadIDCards(config) { `; if (photoSrc) { - photoTagHtml = ``; + photoTagHtml = `
`; } vFront.innerHTML = ` @@ -356,7 +356,7 @@ export async function downloadBulkIDCards({ employees, baseUrl, onStateChange, o `; if (validatedPhotoUrl) { - photoTagHtml = ``; + photoTagHtml = `
`; } vFront.innerHTML = `