import { useEffect, useState } from 'react'; import type { FormEvent } from 'react'; import { useAuth } from '../auth/AuthContext'; import { Alert, LoadingBlock, errorMessage } from '../components/Feedback'; import { Icon } from '../components/Icon'; import { SearchableSelect } from '../components/SearchableSelect'; import { getDocumentDeliverySettingsF4, listDocumentDeliveriesF4, retryDocumentDeliveryF4, retryPendingDocumentDeliveriesF4, updateDocumentDeliverySettingsF4, } from '../lib/documentDeliveryF4Api'; import type { DocumentDeliveryItemF4, DocumentDeliverySettingsF4 } from '../lib/documentDeliveryF4Api'; import { getSmtpSettings, saveSmtpSettings, testSmtpSettings, } from '../lib/smtpSettingsApi'; import type { PublicSmtpSettings, SmtpSecurityMode } from '../lib/smtpSettingsApi'; const statusLabel: Record = { PENDING: 'Pendiente', WAITING_RECIPIENT: 'Falta destinatario', WAITING_TRANSPORT: 'SMTP sin configurar', WAITING_ARTIFACT: 'Documento pendiente', SENT: 'Enviado', FAILED: 'Error', }; function recipientLabel(item: DocumentDeliveryItemF4): string { if (item.recipientAssetName) return item.recipientAssetName; if (item.recipientUserName) return item.recipientUserName; if (item.recipientKind === 'COMPANY') return 'Empresa sin identificar'; if (item.recipientKind === 'OFFICE') return 'Oficina'; return 'Inspector responsable'; } function defaultPort(mode: SmtpSecurityMode): number { return mode === 'TLS' ? 465 : mode === 'STARTTLS' ? 587 : 25; } export function DocumentDeliveryPage() { const { hasPermission, user } = useAuth(); const canManage = hasPermission('document_delivery.manage'); const canSmtpAdmin = Boolean(user?.roles.includes('admin')); const [settings, setSettings] = useState(null); const [smtp, setSmtp] = useState(null); const [items, setItems] = useState([]); const [officeEmail, setOfficeEmail] = useState(''); const [host, setHost] = useState(''); const [port, setPort] = useState('587'); const [securityMode, setSecurityMode] = useState('STARTTLS'); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [fromName, setFromName] = useState('DH Inspección'); const [fromEmail, setFromEmail] = useState(''); const [replyTo, setReplyTo] = useState(''); const [smtpEnabled, setSmtpEnabled] = useState(true); const [testEmail, setTestEmail] = useState(''); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const applySmtp = (value: PublicSmtpSettings) => { setSmtp(value); setHost(value.host ?? ''); setPort(String(value.port ?? defaultPort(value.securityMode ?? 'STARTTLS'))); setSecurityMode(value.securityMode ?? 'STARTTLS'); setUsername(value.username ?? ''); setPassword(''); setFromName(value.fromName ?? 'DH Inspección'); setFromEmail(value.fromEmail ?? ''); setReplyTo(value.replyTo ?? ''); setSmtpEnabled(value.enabled); }; const load = async () => { const [currentSettings, outbox, smtpSettings] = await Promise.all([ getDocumentDeliverySettingsF4(), listDocumentDeliveriesF4(), canSmtpAdmin ? getSmtpSettings() : Promise.resolve(null), ]); setSettings(currentSettings); setOfficeEmail(currentSettings.officeEmail ?? ''); setItems(outbox.data); if (smtpSettings) applySmtp(smtpSettings); }; useEffect(() => { load().catch((requestError) => setError(errorMessage(requestError))).finally(() => setLoading(false)); }, [canSmtpAdmin]); const saveOffice = async (event: FormEvent) => { event.preventDefault(); setSaving(true); setError(''); setSuccess(''); try { const updated = await updateDocumentDeliverySettingsF4(officeEmail.trim() || null); setSettings(updated); setSuccess('Destinatario institucional de oficina actualizado.'); } catch (requestError) { setError(errorMessage(requestError)); } finally { setSaving(false); } }; const saveSmtp = async (event: FormEvent) => { event.preventDefault(); if (!canSmtpAdmin || !host.trim() || !fromName.trim() || !fromEmail.trim()) return; setSaving(true); setError(''); setSuccess(''); try { const input: Parameters[0] = { host: host.trim(), port: Number(port), securityMode, username: username.trim() || null, fromName: fromName.trim(), fromEmail: fromEmail.trim(), replyTo: replyTo.trim() || null, enabled: smtpEnabled, }; if (password) input.password = password; const updated = await saveSmtpSettings(input); applySmtp(updated); setSuccess('Configuración SMTP guardada. La contraseña no se devuelve ni se muestra en pantalla.'); const refreshed = await getDocumentDeliverySettingsF4(); setSettings(refreshed); } catch (requestError) { setError(errorMessage(requestError)); } finally { setSaving(false); } }; const sendTest = async (event: FormEvent) => { event.preventDefault(); if (!canSmtpAdmin || !testEmail.trim()) return; setSaving(true); setError(''); setSuccess(''); try { const result = await testSmtpSettings(testEmail.trim()); setSuccess(`Prueba SMTP enviada a ${result.recipient}.`); } catch (requestError) { setError(errorMessage(requestError)); } finally { setSaving(false); } }; const retryAll = async () => { setSaving(true); setError(''); setSuccess(''); try { const result = await retryPendingDocumentDeliveriesF4(); await load(); setSuccess(`Se procesaron ${result.processed} entrega/s pendientes.`); } catch (requestError) { setError(errorMessage(requestError)); } finally { setSaving(false); } }; if (loading) return ; return
ENTREGA DOCUMENTAL

Correo y envíos

Destinatarios institucionales, trazabilidad de envíos y configuración técnica de correo reservada al Superadmin.

{canManage && }
{error && {error}}{success && {success}}

Destinatarios institucionales

La empresa usa el email de su ficha de Inventario. El Inspector usa el email de su Usuario. Acá se configura únicamente la copia institucional de oficina.

{settings?.smtpConfigured ? 'Salida SMTP operativa' : 'Salida SMTP pendiente'}{settings?.mailFrom ? ` · Remitente ${settings.mailFrom}` : ''}

{canManage &&
}
{canSmtpAdmin &&
SUPERADMIN · SALIDA DE EMAIL

Servidor SMTP

La contraseña se cifra en el servidor con AES-256-GCM. Al volver a esta pantalla sólo se informa si existe una clave guardada; nunca se devuelve su contenido.

{smtp?.source === 'ENVIRONMENT' && Actualmente se está usando la configuración del servidor. Al guardar este formulario, la configuración de base de datos administrable pasará a tener prioridad.}
} {canSmtpAdmin &&

Probar configuración

Envía un correo real con un archivo de prueba usando exactamente la configuración activa.

} {!canSmtpAdmin &&

SMTP reservado al Superadmin. Los usuarios operativos pueden consultar la trazabilidad y, según permisos, gestionar entregas, pero no ver ni modificar credenciales de correo.

}
TRAZABILIDAD

Últimas entregas

Actas PDF a Empresa/Oficina/Inspector e INF Word editable al Inspector responsable.

{items.length}
{items.length === 0 ?
Todavía no existen entregas documentales.
:
{items.map((item) => )}
DocumentoDestinoEstadoIntentos
{item.documentKind === 'ACT_PDF' ? item.actCode : item.reportCode}{item.documentKind === 'ACT_PDF' ? 'Acta PDF inmutable' : 'INF Word editable'}{recipientLabel(item)}{item.recipientEmail ?? 'Sin email configurado'}{statusLabel[item.status]}{item.lastError && {item.lastError}}{item.attempts}{canManage && item.status !== 'SENT' && }
}
; }