Actualizar frontend/src/pages/Comisiones.jsx
This commit is contained in:
@@ -22,6 +22,30 @@ const OPCIONES_POPUP = [
|
|||||||
function toNum(v){ const n=parseFloat(String(v).replace("S/","").replace(/,/g,"").trim()); return isNaN(n)?0:n; }
|
function toNum(v){ const n=parseFloat(String(v).replace("S/","").replace(/,/g,"").trim()); return isNaN(n)?0:n; }
|
||||||
// Formatea cualquier monto a "S/ 1,234" (sin decimales)
|
// Formatea cualquier monto a "S/ 1,234" (sin decimales)
|
||||||
function fmt(v){ return "S/ " + toNum(v).toLocaleString("es-PE",{maximumFractionDigits:0}); }
|
function fmt(v){ return "S/ " + toNum(v).toLocaleString("es-PE",{maximumFractionDigits:0}); }
|
||||||
|
|
||||||
|
// ── Ordenamiento (detecta monto S/, %, fecha dd-mm-yyyy, número o texto) ──
|
||||||
|
function _valorOrden(v) {
|
||||||
|
const s = String(v ?? "").trim();
|
||||||
|
if (s === "" || s === "—" || s === "-") return { t: "vacio", n: 0, s: "" };
|
||||||
|
const mf = s.match(/^(\d{1,2})[-/](\d{1,2})[-/](\d{4})$/);
|
||||||
|
if (mf) { const [, d, m, y] = mf; return { t: "fecha", n: new Date(+y, +m - 1, +d).getTime(), s }; }
|
||||||
|
const limpio = s.replace(/S\/|%|,/g, "").replace(/\s/g, "").trim();
|
||||||
|
if (limpio !== "" && !isNaN(Number(limpio))) return { t: "num", n: Number(limpio), s };
|
||||||
|
return { t: "texto", n: 0, s: s.toLowerCase() };
|
||||||
|
}
|
||||||
|
function _cmp(a, b, dir) {
|
||||||
|
const mult = dir === "asc" ? 1 : -1;
|
||||||
|
const va = _valorOrden(a), vb = _valorOrden(b);
|
||||||
|
if (va.t === "vacio" && vb.t === "vacio") return 0;
|
||||||
|
if (va.t === "vacio") return 1;
|
||||||
|
if (vb.t === "vacio") return -1;
|
||||||
|
if (va.t === "texto" || vb.t === "texto") return mult * va.s.localeCompare(vb.s, "es");
|
||||||
|
return mult * (va.n - vb.n);
|
||||||
|
}
|
||||||
|
function _flecha(activo, dir) {
|
||||||
|
if (!activo) return <span style={{ opacity: 0.3, marginLeft: 4 }}>⇅</span>;
|
||||||
|
return <span style={{ marginLeft: 4 }}>{dir === "asc" ? "▲" : "▼"}</span>;
|
||||||
|
}
|
||||||
// True si la fecha de matrícula (dd/mm/yyyy) es >= 01/01/2025
|
// True si la fecha de matrícula (dd/mm/yyyy) es >= 01/01/2025
|
||||||
function fechaMatDesde2025(fch){
|
function fechaMatDesde2025(fch){
|
||||||
const s = String(fch || "").trim();
|
const s = String(fch || "").trim();
|
||||||
@@ -48,6 +72,16 @@ export default function Comisiones() {
|
|||||||
const [modalBuscar, setModalBuscar] = useState(false);
|
const [modalBuscar, setModalBuscar] = useState(false);
|
||||||
const [modalConfig, setModalConfig] = useState(false);
|
const [modalConfig, setModalConfig] = useState(false);
|
||||||
const cols = useColumnasAjustables([200, 160, 150, 160, 150, 130, 160]);
|
const cols = useColumnasAjustables([200, 160, 150, 160, 150, 130, 160]);
|
||||||
|
const [ordCol, setOrdCol] = useState(null);
|
||||||
|
const [ordDir, setOrdDir] = useState(null);
|
||||||
|
// Mapa columna visible -> índice real en el array de la fila.
|
||||||
|
const IDX_COM = [0, 3, 4, 5, 6, 7];
|
||||||
|
const clickOrden = (i) => {
|
||||||
|
if (i === 6) return; // columna OPCIONES no se ordena
|
||||||
|
if (ordCol !== i) { setOrdCol(i); setOrdDir("asc"); }
|
||||||
|
else if (ordDir === "asc") setOrdDir("desc");
|
||||||
|
else { setOrdCol(null); setOrdDir(null); }
|
||||||
|
};
|
||||||
|
|
||||||
// Carga TODOS los manuales del año (una sola vez por año) → cambio de mes instantáneo
|
// Carga TODOS los manuales del año (una sola vez por año) → cambio de mes instantáneo
|
||||||
function cargarManuales() {
|
function cargarManuales() {
|
||||||
@@ -83,7 +117,14 @@ export default function Comisiones() {
|
|||||||
useEffect(() => { cargarManuales(); }, [ano]);
|
useEffect(() => { cargarManuales(); }, [ano]);
|
||||||
|
|
||||||
const total = useMemo(() => filas.find((f) => f[0] === "TOTAL GENERAL"), [filas]);
|
const total = useMemo(() => filas.find((f) => f[0] === "TOTAL GENERAL"), [filas]);
|
||||||
const datos = useMemo(() => filas.filter((f) => f[0] !== "TOTAL GENERAL"), [filas]);
|
const datos = useMemo(() => {
|
||||||
|
let base = filas.filter((f) => f[0] !== "TOTAL GENERAL");
|
||||||
|
if (ordCol != null && ordDir) {
|
||||||
|
const idx = IDX_COM[ordCol];
|
||||||
|
base = [...base].sort((a, b) => _cmp(a[idx], b[idx], ordDir));
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
}, [filas, ordCol, ordDir]);
|
||||||
|
|
||||||
// Agrupar vendedores manuales por nombre (cada nombre = 1 fila, suma de montos)
|
// Agrupar vendedores manuales por nombre (cada nombre = 1 fila, suma de montos)
|
||||||
const manualesAgrupados = useMemo(() => {
|
const manualesAgrupados = useMemo(() => {
|
||||||
@@ -129,7 +170,15 @@ export default function Comisiones() {
|
|||||||
<div className="table-wrap" style={{overflowX:"auto"}}>
|
<div className="table-wrap" style={{overflowX:"auto"}}>
|
||||||
<table {...cols.tableProps}>
|
<table {...cols.tableProps}>
|
||||||
<cols.ColGroup />
|
<cols.ColGroup />
|
||||||
<thead><tr>{COLS.map((c, i) => <th key={c} style={{position:"relative"}}>{c}<cols.Resizer index={i} /></th>)}</tr></thead>
|
<thead><tr>{COLS.map((c, i) => {
|
||||||
|
const ordenable = i !== 6;
|
||||||
|
return (
|
||||||
|
<th key={c} onClick={()=>clickOrden(i)}
|
||||||
|
style={{position:"relative", cursor: ordenable?"pointer":"default", userSelect:"none"}}>
|
||||||
|
{c}{ordenable && _flecha(ordCol===i, ordDir)}<cols.Resizer index={i} />
|
||||||
|
</th>
|
||||||
|
);
|
||||||
|
})}</tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{datos.map((f, i) => (
|
{datos.map((f, i) => (
|
||||||
<tr key={i}>
|
<tr key={i}>
|
||||||
|
|||||||
Reference in New Issue
Block a user