// ROCA GOLF — Reporte IA: análisis de presupuesto por proyecto (Claude) // El servidor calcula todos los montos (SQL real); la IA solo narra sobre ellos: // claves de riesgo, oportunidades de eficiencia, una propuesta global de presupuesto // (estimación, no dato oficial) y una calificación de qué tan bien se presupuestó. // Cada clic en "Generar" produce un análisis fresco y lo guarda en el historial. const RIA_SEV_CLASS = { alto: 'over', medio: 'alert', bajo: 'warn' }; const RIA_SEV_LABEL = { alto: 'Riesgo alto', medio: 'Riesgo medio', bajo: 'Riesgo bajo' }; function RiaSevTag({ sev }) { const cls = RIA_SEV_CLASS[sev] || 'alert'; return {RIA_SEV_LABEL[sev] || 'Riesgo'}; } // Lee los colores de severidad de las CSS vars (mismo patrón que rgTheme, que no // expone warn/alert porque esas gráficas no las necesitaban hasta ahora). function riaFlagColors() { const cs = getComputedStyle(document.documentElement); const v = (name, fb) => { const x = (cs.getPropertyValue(name) || '').trim(); return x || fb; }; return { ok: v('--rg-flag-ok', '#2d6a44'), warn: v('--rg-flag-warn', '#d4a017'), alert: v('--rg-flag-alert', '#c8651b'), over: v('--rg-flag-over', '#b22222'), }; } function riaMoney(n) { try { return '$' + fmtMoney(Number(n) || 0); } catch { return '$' + (Number(n) || 0).toFixed(2); } } function RiaKpi({ label, value, valueColor, sub }) { return (
| migIncluir[migKey(it)])} onChange={migToggleTodos}/> | Clave canónica | Grupo | Clave a usar en Contraloría | Descripción | Monto |
|---|---|---|---|---|---|
| setMigIncluir(prev => ({ ...prev, [k]: !prev[k] }))}/> | {it.clave_canonica} | {it.grupo} |
setMigClave(prev => ({ ...prev, [k]: e.target.value }))}
style={{ width: 110 }}/>
{it.sin_match_grupo && (
⚠ {it.clave_sugerida ? `sugerida (${it.match_score}% similar)` : 'sin coincidencia — revisa manualmente'}
)}
|
{it.descripcion} | {riaMoney(it.monto)} |
{typeof s.detail === 'string' ? s.detail : JSON.stringify(s.detail, null, 2)}
)}
| Fecha | Generado por | Modelo | Score | |
|---|---|---|---|---|
| {h.generado_en} | {h.generado_por_nombre || '—'} | {h.modelo || '—'} | {h.score_calificacion != null ? {h.score_calificacion}/100 : '—'} |
| 0 && dd.top_sin_presupuesto.every(c => selSinPresu[c.insumoid])} onChange={() => toggleSelTodasSinPresu(dd.top_sin_presupuesto)}/> | Clave | Descripción | Familia | Gasto real | Monto a presupuestar (MXN) |
|---|---|---|---|---|---|
| toggleSelSinPresu(c.insumoid, c.gasto)}/> | {c.clave} |
{c.descripcion}
{c.posible_duplicado && (
⚠ ¿Quisiste decir {c.posible_duplicado.clave} · {c.posible_duplicado.descripcion} ({Math.round(c.posible_duplicado.score * 100)}% similar)?
)}
|
{c.familia} | {riaMoney(c.gasto)} |
{c.tiene_limite_contraloria ? (
<>
Definido en Contraloría
>
) : (
setMontoSinPresu(prev => ({ ...prev, [c.insumoid]: e.target.value }))}
style={{ width: 130 }}/>
)}
|
{p}
))}| Clave | Descripción | Severidad | Presupuestado | Gasto real | Motivo | Recomendación |
|---|---|---|---|---|---|---|
| {r.clave} | {r.descripcion} | {r.presupuestado != null ? riaMoney(r.presupuestado) : '—'} | {riaMoney(r.gasto)} | {r.motivo} | {r.recomendacion} |
{ia.propuesta_global.justificacion}
{ia.propuesta_global.supuestos && ({ia.calificacion_presupuesto.explicacion}