F3.1 WEB: mostrar entregas documentales al Inspector
This commit is contained in:
@@ -1,16 +1,144 @@
|
||||
import { FormEvent, useEffect, useState } from 'react';
|
||||
import { useAuth } from '../auth/AuthContext';
|
||||
import { Alert, LoadingBlock, errorMessage } from '../components/Feedback';
|
||||
import { Icon } from '../components/Icon';
|
||||
import { getDocumentDeliverySettings, listDocumentDeliveries, retryDocumentDelivery, retryPendingDocumentDeliveries, updateDocumentDeliverySettings } from '../lib/api';
|
||||
import {
|
||||
getDocumentDeliverySettings,
|
||||
listDocumentDeliveries,
|
||||
retryDocumentDelivery,
|
||||
retryPendingDocumentDeliveries,
|
||||
updateDocumentDeliverySettings,
|
||||
} from '../lib/api';
|
||||
import type { DocumentDeliveryItem, DocumentDeliverySettings } from '../lib/api';
|
||||
import { useAuth } from '../auth/AuthContext';
|
||||
|
||||
const statusLabel:Record<DocumentDeliveryItem['status'],string>={PENDING:'Pendiente',WAITING_RECIPIENT:'Falta destinatario',WAITING_TRANSPORT:'SMTP sin configurar',WAITING_ARTIFACT:'Documento pendiente',SENT:'Enviado',FAILED:'Error'};
|
||||
export function DocumentDeliveryPage(){ const {hasPermission}=useAuth(); const canManage=hasPermission('document_delivery.manage'); const [settings,setSettings]=useState<DocumentDeliverySettings|null>(null); const [items,setItems]=useState<DocumentDeliveryItem[]>([]); const [officeEmail,setOfficeEmail]=useState(''); const [directorEmail,setDirectorEmail]=useState(''); const [loading,setLoading]=useState(true); const [saving,setSaving]=useState(false); const [error,setError]=useState(''); const [success,setSuccess]=useState('');
|
||||
const load=async()=>{const [s,o]=await Promise.all([getDocumentDeliverySettings(),listDocumentDeliveries()]);setSettings(s);setOfficeEmail(s.officeEmail??'');setDirectorEmail(s.directorEmail??'');setItems(o.data);}; useEffect(()=>{load().catch(e=>setError(errorMessage(e))).finally(()=>setLoading(false));},[]);
|
||||
const save=async(e:FormEvent)=>{e.preventDefault();setSaving(true);setError('');setSuccess('');try{const s=await updateDocumentDeliverySettings({officeEmail:officeEmail||null,directorEmail:directorEmail||null});setSettings(s);setSuccess('Destinatarios institucionales actualizados');}catch(err){setError(errorMessage(err));}finally{setSaving(false);}};
|
||||
const retryAll=async()=>{setSaving(true);setError('');setSuccess('');try{const r=await retryPendingDocumentDeliveries();await load();setSuccess(`Se procesaron ${r.processed} entrega/s pendientes.`);}catch(err){setError(errorMessage(err));}finally{setSaving(false);}};
|
||||
if(loading)return <LoadingBlock label="Cargando entrega documental…"/>; return <section className="narrow-section"><div className="page-heading"><div><span className="eyebrow">DOCUMENTOS</span><h1>Entrega documental</h1><p>Configurá los destinatarios institucionales y controlá el envío automático de Actas e Informes.</p></div>{canManage&&<button className="button secondary" onClick={retryAll} disabled={saving}><Icon name="history"/>Reintentar pendientes</button>}</div>{error&&<Alert>{error}</Alert>}{success&&<Alert type="success">{success}</Alert>}
|
||||
<form className="panel form-panel" onSubmit={save}><div className="form-section"><div><h2>Destinatarios institucionales</h2><p className="section-copy">El email de cada empresa se configura dentro de su ficha de Inventario. Las credenciales SMTP permanecen fuera de la base de datos.</p></div><div className="form-grid"><label className="field"><span>Email de oficina</span><input type="email" value={officeEmail} onChange={e=>setOfficeEmail(e.target.value)} disabled={!canManage}/></label><label className="field"><span>Email del Director de Hidrocarburos</span><input type="email" value={directorEmail} onChange={e=>setDirectorEmail(e.target.value)} disabled={!canManage}/></label></div><div className="temporal-notice"><Icon name={settings?.smtpConfigured?'check':'alert'}/><p><strong>{settings?.smtpConfigured?'Servidor de correo configurado':'Servidor de correo pendiente'}</strong>{settings?.mailFrom?` · Remitente ${settings.mailFrom}`:' · Falta configurar SMTP_HOST y MAIL_FROM en el servidor.'}</p></div>{canManage&&<div className="form-actions"><button className="button primary" disabled={saving}><Icon name="check"/>{saving?'Guardando…':'Guardar destinatarios'}</button></div>}</div></form>
|
||||
<div className="panel"><div className="panel-heading"><div><span className="eyebrow">TRAZABILIDAD</span><h2>Últimas entregas</h2></div><span>{items.length}</span></div>{items.length===0?<div className="inline-empty">Todavía no existen entregas documentales.</div>:<div className="table-wrap"><table><thead><tr><th>Documento</th><th>Destino</th><th>Estado</th><th>Intentos</th><th></th></tr></thead><tbody>{items.map(item=><tr key={item.id}><td><strong>{item.documentKind==='ACT_PDF'?item.actCode:item.reportCode}</strong><small className="block-muted">{item.documentKind==='ACT_PDF'?'Acta PDF':'Informe Word'}</small></td><td>{item.recipientAssetName??(item.recipientKind==='COMPANY'?'Empresa sin identificar':item.recipientKind==='OFFICE'?'Oficina':'Director')}<small className="block-muted">{item.recipientEmail??'Sin email configurado'}</small></td><td><span className={`status-badge ${item.status==='SENT'?'active':item.status==='FAILED'?'inactive':'pending'}`}>{statusLabel[item.status]}</span>{item.lastError&&<small className="block-muted">{item.lastError}</small>}</td><td>{item.attempts}</td><td>{canManage&&item.status!=='SENT'&&<button className="button text" type="button" disabled={saving} onClick={async()=>{setSaving(true);try{await retryDocumentDelivery(item.id);await load();}catch(err){setError(errorMessage(err));}finally{setSaving(false);}}}>Reintentar</button>}</td></tr>)}</tbody></table></div>}</div>
|
||||
</section>; }
|
||||
const statusLabel: Record<DocumentDeliveryItem['status'], string> = {
|
||||
PENDING: 'Pendiente',
|
||||
WAITING_RECIPIENT: 'Falta destinatario',
|
||||
WAITING_TRANSPORT: 'SMTP sin configurar',
|
||||
WAITING_ARTIFACT: 'Documento pendiente',
|
||||
SENT: 'Enviado',
|
||||
FAILED: 'Error',
|
||||
};
|
||||
|
||||
type DeliveryWithInspector = DocumentDeliveryItem & {
|
||||
recipientUserId?: string | null;
|
||||
recipientUserName?: string | null;
|
||||
};
|
||||
|
||||
function recipientLabel(item: DeliveryWithInspector): string {
|
||||
if (item.recipientAssetName) return item.recipientAssetName;
|
||||
if (item.recipientUserName) return item.recipientUserName;
|
||||
const kind = String(item.recipientKind);
|
||||
if (kind === 'COMPANY') return 'Empresa sin identificar';
|
||||
if (kind === 'OFFICE') return 'Oficina';
|
||||
if (kind === 'INSPECTOR') return 'Inspector responsable';
|
||||
return 'Director';
|
||||
}
|
||||
|
||||
export function DocumentDeliveryPage() {
|
||||
const { hasPermission } = useAuth();
|
||||
const canManage = hasPermission('document_delivery.manage');
|
||||
const [settings, setSettings] = useState<DocumentDeliverySettings | null>(null);
|
||||
const [items, setItems] = useState<DocumentDeliveryItem[]>([]);
|
||||
const [officeEmail, setOfficeEmail] = useState('');
|
||||
const [directorEmail, setDirectorEmail] = useState('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [success, setSuccess] = useState('');
|
||||
|
||||
const load = async () => {
|
||||
const [currentSettings, outbox] = await Promise.all([
|
||||
getDocumentDeliverySettings(),
|
||||
listDocumentDeliveries(),
|
||||
]);
|
||||
setSettings(currentSettings);
|
||||
setOfficeEmail(currentSettings.officeEmail ?? '');
|
||||
setDirectorEmail(currentSettings.directorEmail ?? '');
|
||||
setItems(outbox.data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
load().catch((requestError) => setError(errorMessage(requestError))).finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const save = async (event: FormEvent) => {
|
||||
event.preventDefault();
|
||||
setSaving(true);
|
||||
setError('');
|
||||
setSuccess('');
|
||||
try {
|
||||
const currentSettings = await updateDocumentDeliverySettings({
|
||||
officeEmail: officeEmail || null,
|
||||
directorEmail: directorEmail || null,
|
||||
});
|
||||
setSettings(currentSettings);
|
||||
setSuccess('Destinatarios institucionales actualizados');
|
||||
} catch (requestError) {
|
||||
setError(errorMessage(requestError));
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const retryAll = async () => {
|
||||
setSaving(true);
|
||||
setError('');
|
||||
setSuccess('');
|
||||
try {
|
||||
const result = await retryPendingDocumentDeliveries();
|
||||
await load();
|
||||
setSuccess(`Se procesaron ${result.processed} entrega/s pendientes.`);
|
||||
} catch (requestError) {
|
||||
setError(errorMessage(requestError));
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <LoadingBlock label="Cargando entrega documental…" />;
|
||||
|
||||
return <section className="narrow-section">
|
||||
<div className="page-heading">
|
||||
<div>
|
||||
<span className="eyebrow">DOCUMENTOS</span>
|
||||
<h1>Entrega documental</h1>
|
||||
<p>Configurá los destinatarios institucionales y controlá el envío automático de Actas e Informes.</p>
|
||||
</div>
|
||||
{canManage && <button className="button secondary" onClick={retryAll} disabled={saving}><Icon name="history" />Reintentar pendientes</button>}
|
||||
</div>
|
||||
{error && <Alert>{error}</Alert>}
|
||||
{success && <Alert type="success">{success}</Alert>}
|
||||
|
||||
<form className="panel form-panel" onSubmit={save}>
|
||||
<div className="form-section">
|
||||
<div>
|
||||
<h2>Destinatarios institucionales</h2>
|
||||
<p className="section-copy">El email de cada empresa se configura dentro de su ficha de Inventario. El Inspector responsable usa automáticamente el email declarado en su Usuario. Las credenciales SMTP permanecen fuera de la base de datos.</p>
|
||||
</div>
|
||||
<div className="form-grid">
|
||||
<label className="field"><span>Email de oficina</span><input type="email" value={officeEmail} onChange={(event) => setOfficeEmail(event.target.value)} disabled={!canManage} /></label>
|
||||
<label className="field"><span>Email del Director de Hidrocarburos</span><input type="email" value={directorEmail} onChange={(event) => setDirectorEmail(event.target.value)} disabled={!canManage} /></label>
|
||||
</div>
|
||||
<div className="temporal-notice"><Icon name={settings?.smtpConfigured ? 'check' : 'alert'} /><p><strong>{settings?.smtpConfigured ? 'Servidor de correo configurado' : 'Servidor de correo pendiente'}</strong>{settings?.mailFrom ? ` · Remitente ${settings.mailFrom}` : ' · Falta configurar SMTP_HOST y MAIL_FROM en el servidor.'}</p></div>
|
||||
{canManage && <div className="form-actions"><button className="button primary" disabled={saving}><Icon name="check" />{saving ? 'Guardando…' : 'Guardar destinatarios'}</button></div>}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="panel">
|
||||
<div className="panel-heading"><div><span className="eyebrow">TRAZABILIDAD</span><h2>Últimas entregas</h2></div><span>{items.length}</span></div>
|
||||
{items.length === 0
|
||||
? <div className="inline-empty">Todavía no existen entregas documentales.</div>
|
||||
: <div className="table-wrap"><table><thead><tr><th>Documento</th><th>Destino</th><th>Estado</th><th>Intentos</th><th></th></tr></thead><tbody>{items.map((baseItem) => {
|
||||
const item = baseItem as DeliveryWithInspector;
|
||||
return <tr key={item.id}>
|
||||
<td><strong>{item.documentKind === 'ACT_PDF' ? item.actCode : item.reportCode}</strong><small className="block-muted">{item.documentKind === 'ACT_PDF' ? 'Acta PDF' : 'Informe Word'}</small></td>
|
||||
<td>{recipientLabel(item)}<small className="block-muted">{item.recipientEmail ?? 'Sin email configurado'}</small></td>
|
||||
<td><span className={`status-badge ${item.status === 'SENT' ? 'active' : item.status === 'FAILED' ? 'inactive' : 'pending'}`}>{statusLabel[item.status]}</span>{item.lastError && <small className="block-muted">{item.lastError}</small>}</td>
|
||||
<td>{item.attempts}</td>
|
||||
<td>{canManage && item.status !== 'SENT' && <button className="button text" type="button" disabled={saving} onClick={async () => { setSaving(true); try { await retryDocumentDelivery(item.id); await load(); } catch (requestError) { setError(errorMessage(requestError)); } finally { setSaving(false); } }}>Reintentar</button>}</td>
|
||||
</tr>;
|
||||
})}</tbody></table></div>}
|
||||
</div>
|
||||
</section>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user