// ROCA GOLF — Lista de OCs, Insumos, Proveedores, Historial (conectado a API) // ───── ORDENES DE COMPRA ───── function OCListView({ onNav }) { const { ordenes, proyectos, familias, alerts, loading } = useAppData(); const [filters, setFilters] = useState({ folio: "", prov: "", proy: "", status: "", flujo: "", prior: "" }); const alertCount = alerts.filter(a => a.level !== "ok").length; // Nombre de proveedor/proyecto: usa el campo del API si existe, si no busca en lista const getProv = (o) => o.proveedor_nombre || nombreProveedor(o.proveedorid); const getProy = (o) => o.proyecto_nombre || nombreProyecto(o.proyectoid); const filtered = ordenes.filter(o => { if (filters.folio && !String(o.folio).includes(filters.folio)) return false; if (filters.prov) { if (!getProv(o).toLowerCase().includes(filters.prov.toLowerCase())) return false; } if (filters.proy && o.proyectoid !== +filters.proy) return false; if (filters.status && o.statuspago !== filters.status) return false; if (filters.flujo && o.statusflujo !== filters.flujo) return false; if (filters.prior && o.prioridad !== filters.prior) return false; return true; }); return (
{loading && } {Icon.plus()} Nueva OC}/>
setFilters({...filters, folio: e.target.value})}/>
setFilters({...filters, prov: e.target.value})}/>

{filtered.length} órdenes de compra

Total {filtered.length} de {ordenes.length}
{filtered.length === 0 && !loading ? (
{Icon.oc(28)}

No se encontraron órdenes con los filtros aplicados.

) : (
{filtered.map(o => ( ))}
FolioFechaProyectoProveedor TotalPendiente StatusFlujoPrioridad
{fmtFolio(o.folio)} {fmtFecha(o.fecha)} {getProy(o)} {getProv(o)} {o.moneda === "USD" ? "USD " : "$"}{fmtMoney(o.total)} 0 ? "var(--rg-flag-over)" : "var(--rg-muted)" }}> {o.montopendiente > 0 ? "$" + fmtMoney(o.montopendiente) : "—"}
)}
Mostrando 1–{filtered.length} de {filtered.length}
); } // ───── INSUMOS ───── function InsumosView({ onNav }) { const { insumos, familias, alerts, loading } = useAppData(); const [familiaFiltro, setFamiliaFiltro] = useState(""); const [q, setQ] = useState(""); const [verInactivos, setVerInactivos] = useState(false); const alertCount = alerts.filter(a => a.level !== "ok").length; const familiasUnicas = useMemo(() => { const map = {}; insumos.forEach(i => { map[i.codigofamilia] = i.nombrefamilia; }); return Object.entries(map).sort(([a], [b]) => a.localeCompare(b)); }, [insumos]); const filtered = insumos.filter(i => { const esActivo = Boolean(i.activo); if (!verInactivos && !esActivo) return false; if (verInactivos && esActivo) return false; if (familiaFiltro && i.codigofamilia !== familiaFiltro) return false; if (q) { const ql = q.toLowerCase(); if (!i.clave.toLowerCase().includes(ql) && !i.descripcion.toLowerCase().includes(ql)) return false; } return true; }); return (
{loading && } {Icon.plus()} Nuevo insumo}/>
setQ(e.target.value)}/>

{filtered.length} insumos

Catálogo {verInactivos ? "inactivo" : "activo"}
ClaveDescripciónFamilia UsosEstadoAcciones
{filtered.length === 0 && !loading ? (
{Icon.catalog(28)}

Sin insumos que coincidan con la búsqueda.

) : filtered.map(i => (
{i.clave} {i.descripcion} {i.codigofamilia} · {i.nombrefamilia} {i.usos || 0} OCs {i.activo ? "Activo" : "Inactivo"}
))}
); } // ───── PROVEEDORES ───── function ProveedoresView({ onNav }) { const { proveedores, alerts, loading } = useAppData(); const [tab, setTab] = useState("activos"); const [q, setQ] = useState(""); const alertCount = alerts.filter(a => a.level !== "ok").length; const activos = proveedores.filter(p => p.activo); const inactivos = proveedores.filter(p => !p.activo); const base = tab === "activos" ? activos : inactivos; const list = base.filter(p => { if (!q) return true; const ql = q.toLowerCase(); return ( p.nombre.toLowerCase().includes(ql) || (p.rfc || "").toLowerCase().includes(ql) || (p.contacto|| "").toLowerCase().includes(ql) || (p.email || "").toLowerCase().includes(ql) ); }).sort((a, b) => (b.monto || 0) - (a.monto || 0)); return (
{loading && } {Icon.plus()} Nuevo proveedor}/>
{Icon.search()} setQ(e.target.value)} />

Cartera de proveedores

{list.length} {tab === "activos" ? "activos" : "inactivos"} · ordenados por monto ejercido
{list.length === 0 && !loading ? (
{Icon.vendors(28)}

Sin proveedores que coincidan con la búsqueda.

) : (
{list.map(p => (
{p.nombre.split(" ").slice(0, 2).map(w => w[0]).join("").toUpperCase()}
{p.nombre}
{p.rfc || "Sin RFC"} · {p.banco || "—"} · {p.moneda || "MXN"}
{p.ocs || 0} OCs ${fmtMoney(p.monto || 0)}{" "} {p.moneda || "MXN"} {p.contacto && {p.contacto}}
))}
)}
); } // ───── HISTORIAL ───── function HistorialView({ onNav }) { const { historial, alerts, loading } = useAppData(); const [filter, setFilter] = useState({ accion: "", tabla: "" }); const alertCount = alerts.filter(a => a.level !== "ok").length; // Normaliza el campo descripción (API usa 'descripcion', mock usaba 'desc') const getDesc = (h) => h.descripcion || h.desc || "—"; const filtered = historial.filter(h => { if (filter.accion && !h.accion.startsWith(filter.accion)) return false; if (filter.tabla && (h.tabla || h.tablaafectada || "") !== filter.tabla) return false; return true; }); const accionMeta = (a) => { if (a.startsWith("CREAR")) return { c: "var(--rg-flag-ok)", bg: "rgba(45,106,68,0.12)", ic: Icon.plus }; if (a.startsWith("EDITAR")) return { c: "#1976d2", bg: "rgba(25,118,210,0.10)", ic: Icon.edit }; if (a.startsWith("ELIMINAR"))return { c: "var(--rg-flag-over)", bg: "rgba(178,34,34,0.10)", ic: Icon.x }; if (a.startsWith("PAGO")) return { c: "var(--rg-flag-ok)", bg: "rgba(45,106,68,0.12)", ic: Icon.budget }; if (a.startsWith("LOGIN")) return { c: "var(--rg-muted)", bg: "var(--rg-cream)", ic: Icon.eye }; if (a.startsWith("LOGOUT")) return { c: "var(--rg-muted)", bg: "var(--rg-cream)", ic: Icon.history}; if (a.startsWith("CAMBIO") || a.startsWith("CAMBIAR")) return { c: "var(--rg-flag-warn)", bg: "rgba(212,160,23,0.12)", ic: Icon.history}; return { c: "var(--rg-muted)", bg: "var(--rg-cream)", ic: Icon.history }; }; return (
{loading && } {Icon.excel()} Exportar}/>

{filtered.length} eventos

Bitácora del sistema · ordenado por fecha desc
FechaUsuario Acción · descripciónTabla
{filtered.length === 0 && !loading ? (
{Icon.history(28)}

Sin eventos que coincidan con el filtro.

) : filtered.map(h => { const m = accionMeta(h.accion); const tabla = h.tabla || h.tablaafectada || ""; return (
{m.ic(13)}
{h.fecha} {h.usuario || "—"}
{h.accion} {getDesc(h)}
{tabla || "—"}
); })}
Mostrando 1–{filtered.length} de {historial.length}
); } Object.assign(window, { OCListView, InsumosView, ProveedoresView, HistorialView });