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)} /> -