frontend-plus-react 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -456,8 +456,9 @@ var import_toolkit2 = require("@reduxjs/toolkit");
456
456
  var initialState2 = {
457
457
  currentPath: "/",
458
458
  previousPath: null,
459
- redirectPath: null
459
+ redirectPath: null,
460
460
  // <--- Inicializamos en null
461
+ redirectUsername: null
461
462
  };
462
463
  var routerSlice = (0, import_toolkit2.createSlice)({
463
464
  name: "router",
@@ -469,15 +470,20 @@ var routerSlice = (0, import_toolkit2.createSlice)({
469
470
  },
470
471
  setRedirectPath: (state, action) => {
471
472
  state.redirectPath = action.payload;
473
+ if (action.payload === null) state.redirectUsername = null;
474
+ },
475
+ setRedirectUsername: (state, action) => {
476
+ state.redirectUsername = action.payload;
472
477
  },
473
478
  resetRouterState: (state) => {
474
479
  state.currentPath = "/";
475
480
  state.previousPath = null;
476
481
  state.redirectPath = null;
482
+ state.redirectUsername = null;
477
483
  }
478
484
  }
479
485
  });
480
- var { setCurrentPath, setRedirectPath, resetRouterState } = routerSlice.actions;
486
+ var { setCurrentPath, setRedirectPath, setRedirectUsername, resetRouterState } = routerSlice.actions;
481
487
  var routerSlice_default = routerSlice.reducer;
482
488
 
483
489
  // src/store/menuUiSlice.ts
@@ -502,13 +508,23 @@ var menuUiSlice = (0, import_toolkit3.createSlice)({
502
508
  toggleSubMenu: (state, action) => {
503
509
  state.subMenuOpenStates[action.payload] = !state.subMenuOpenStates[action.payload];
504
510
  },
505
- // --- NUEVO REDUCER: Establecer el estado de todos los submenús ---
506
- setAllSubMenusOpen: (state, action) => {
507
- for (const menuName in state.subMenuOpenStates) {
508
- if (Object.prototype.hasOwnProperty.call(state.subMenuOpenStates, menuName)) {
509
- state.subMenuOpenStates[menuName] = action.payload;
511
+ /** Acordeón: abre un grupo y cierra a sus hermanos (con todo lo que tienen adentro). */
512
+ abrirGrupo: (state, action) => {
513
+ for (const clave of Object.keys(state.subMenuOpenStates)) {
514
+ if (action.payload.hermanos.some((hermano) => clave === hermano || clave.startsWith(hermano + "/"))) {
515
+ state.subMenuOpenStates[clave] = false;
510
516
  }
511
517
  }
518
+ state.subMenuOpenStates[action.payload.clave] = true;
519
+ },
520
+ /** Deja abiertos solo los grupos de este camino (el del ítem que se está mostrando). */
521
+ abrirCamino: (state, action) => {
522
+ for (const clave of Object.keys(state.subMenuOpenStates)) {
523
+ state.subMenuOpenStates[clave] = action.payload.includes(clave);
524
+ }
525
+ for (const clave of action.payload) {
526
+ state.subMenuOpenStates[clave] = true;
527
+ }
512
528
  },
513
529
  resetMenuUiState: (state) => {
514
530
  Object.assign(state, initialState3);
@@ -520,8 +536,8 @@ var {
520
536
  toggleDrawer,
521
537
  setSubMenuOpen,
522
538
  toggleSubMenu,
523
- setAllSubMenusOpen,
524
- // ¡Exportamos la nueva acción!
539
+ abrirGrupo,
540
+ abrirCamino,
525
541
  resetMenuUiState
526
542
  } = menuUiSlice.actions;
527
543
  var menuUiSlice_default = menuUiSlice.reducer;
@@ -1425,8 +1441,12 @@ function BotonDetalle({ row, rowId, detailTable, deshabilitado }) {
1425
1441
  import_material3.IconButton,
1426
1442
  {
1427
1443
  size: "small",
1444
+ sx: { width: 24, height: 24, p: 0 },
1428
1445
  disabled: deshabilitado,
1429
1446
  onClick: (e) => alternarDetalle(row, rowId, detailTable, e.ctrlKey || e.metaKey),
1447
+ onMouseDown: (e) => {
1448
+ if (e.button === 1) e.preventDefault();
1449
+ },
1430
1450
  onAuxClick: (e) => {
1431
1451
  if (e.button === 1) alternarDetalle(row, rowId, detailTable, true);
1432
1452
  },
@@ -2048,9 +2068,10 @@ var defaultColumnHeaderCellRenderer = (props, fieldDef, ocultarColumna) => {
2048
2068
  {
2049
2069
  variant: "body2",
2050
2070
  sx: {
2051
- fontWeight: "bold",
2071
+ fontWeight: 500,
2072
+ color: "#37474f",
2052
2073
  textDecoration: isPrimaryKey ? "underline" : "none",
2053
- fontSize: "0.930rem",
2074
+ fontSize: "0.85rem",
2054
2075
  textAlign: "left",
2055
2076
  flexGrow: 1,
2056
2077
  overflow: "hidden",
@@ -2076,22 +2097,20 @@ var ActionsColumnHeaderCell = ({
2076
2097
  allowInsert && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2077
2098
  import_material4.IconButton,
2078
2099
  {
2079
- color: "inherit",
2080
2100
  onClick: () => handleAddRow(),
2081
2101
  size: "small",
2082
2102
  title: "A\xF1adir registro",
2083
- sx: { p: 0.25 },
2084
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_Add.default, { sx: { fontSize: 18, color: "success.main" } })
2103
+ sx: { p: 0.25, color: "grey.800", "&:hover": { color: "success.main" } },
2104
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_Add.default, { sx: { fontSize: 18 } })
2085
2105
  }
2086
2106
  ),
2087
2107
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2088
2108
  import_material4.IconButton,
2089
2109
  {
2090
- color: "inherit",
2091
2110
  onClick: toggleFilterVisibility,
2092
2111
  size: "small",
2093
2112
  title: isFilterRowVisible ? "Ocultar filtros" : "Mostrar filtros",
2094
- sx: { p: 0.25 },
2113
+ sx: { p: 0.25, color: "grey.800" },
2095
2114
  children: isFilterRowVisible ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_SearchOff.default, { sx: { fontSize: 18 } }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_Search.default, { sx: { fontSize: 18 } })
2096
2115
  }
2097
2116
  ),
@@ -2101,13 +2120,13 @@ var ActionsColumnHeaderCell = ({
2101
2120
  onClick: (event) => handleDataGridOptions(event),
2102
2121
  size: "small",
2103
2122
  title: "Opciones de esta tabla",
2104
- sx: { p: 0.25 },
2123
+ sx: { p: 0.25, color: "grey.800" },
2105
2124
  children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_MoreVert.default, { sx: { fontSize: 20, variant: "filled", size: "small" } })
2106
2125
  }
2107
2126
  )
2108
2127
  ] });
2109
2128
  };
2110
- var detailColumnCellHeaderRenderer = (props, detailTable) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_material4.Tooltip, { title: detailTable.label, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_material4.Typography, { variant: "caption", sx: { fontSize: "0.75rem", fontWeight: "bold" }, children: detailTable.abr.toUpperCase() }) });
2129
+ var detailColumnCellHeaderRenderer = (props, detailTable) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_material4.Tooltip, { title: detailTable.label, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_material4.Box, { sx: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_material4.Typography, { variant: "caption", sx: { fontSize: "0.75rem", fontWeight: "bold" }, children: detailTable.abr.toUpperCase() }) }) });
2111
2130
 
2112
2131
  // src/components/grid/FilterInputRender.tsx
2113
2132
  var import_material9 = require("@mui/material");
@@ -2582,9 +2601,12 @@ var TypedField = ({ fieldDef, value, onChange, onCommit, disabled, isFilterMode
2582
2601
  "& .MuiOutlinedInput-root": {
2583
2602
  backgroundColor: "background.paper",
2584
2603
  height: FILTER_ROW_HEIGHT - 5,
2585
- minWidth: 50,
2604
+ minWidth: 30,
2586
2605
  fontSize: "0.8rem",
2587
2606
  padding: "0px"
2607
+ },
2608
+ "& .MuiOutlinedInput-input": {
2609
+ padding: "0 4px"
2588
2610
  }
2589
2611
  } : {
2590
2612
  "& .MuiOutlinedInput-root": {
@@ -2608,7 +2630,10 @@ var TypedField = ({ fieldDef, value, onChange, onCommit, disabled, isFilterMode
2608
2630
  };
2609
2631
 
2610
2632
  // src/components/grid/FilterInputRender.tsx
2611
- var import_jsx_runtime12 = require("react/jsx-runtime");
2633
+ var import_jsx_runtime12 = (
2634
+ // como en backend-plus, el operador es un símbolo chico y el campo se queda con el resto del ancho
2635
+ require("react/jsx-runtime")
2636
+ );
2612
2637
  function FilterInputRenderer({
2613
2638
  columnKey,
2614
2639
  currentFilter,
@@ -2631,7 +2656,7 @@ function FilterInputRenderer({
2631
2656
  [columnKey]: { operator: newOp, value: formattedVal }
2632
2657
  }));
2633
2658
  };
2634
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Box, { sx: { mt: 0, display: "flex", alignItems: "center", gap: 0.5, pr: 1 }, children: [
2659
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Box, { sx: { mt: 0, display: "flex", alignItems: "center", gap: "2px", width: "100%" }, children: [
2635
2660
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2636
2661
  "select",
2637
2662
  {
@@ -2639,13 +2664,16 @@ function FilterInputRenderer({
2639
2664
  onChange: (e) => handleFilterChange(e.target.value, val),
2640
2665
  style: {
2641
2666
  height: FILTER_ROW_HEIGHT - 5,
2642
- minWidth: 40,
2643
- backgroundColor: "white",
2644
- border: "1px solid #ccc",
2667
+ width: 22,
2668
+ flexShrink: 0,
2669
+ appearance: "none",
2670
+ backgroundColor: "#CEF",
2671
+ border: "none",
2645
2672
  borderRadius: "4px",
2646
2673
  fontSize: "0.8rem",
2647
- padding: "0 2px",
2674
+ padding: 0,
2648
2675
  textAlign: "center",
2676
+ textAlignLast: "center",
2649
2677
  cursor: "pointer"
2650
2678
  },
2651
2679
  children: filterOperators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: op.value, children: op.label }, op.value))
@@ -2696,7 +2724,7 @@ var defaultColumnSummaryCellRenderer = (props, fixedFields) => {
2696
2724
  const { filters, setFilters } = row;
2697
2725
  if (valorFijoDe(fixedFields, column.key)) return null;
2698
2726
  const fieldDef = esColumnaPropia(column) && column.customType === "default" ? column.fieldDef : void 0;
2699
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material10.Box, { sx: { width: "100%", height: "100%", display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2727
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material10.Box, { sx: { position: "absolute", inset: 0, display: "flex", alignItems: "center", px: "2px" }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2700
2728
  FilterInputRender_default,
2701
2729
  {
2702
2730
  columnKey: column.key,
@@ -2923,14 +2951,12 @@ var allColumnsCellRenderer = (props) => {
2923
2951
  }
2924
2952
  ];
2925
2953
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_material13.Box, { sx: { display: "flex", justifyContent: "center", alignItems: "center", height: "100%", gap: 0.5 }, children: gridActionButtons.map((actionDef) => tableDefinition.allow[actionDef.action] ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2926
- import_material13.Button,
2954
+ import_material13.IconButton,
2927
2955
  {
2928
- variant: "outlined",
2929
- color: actionDef.color,
2930
2956
  size: "small",
2931
2957
  onClick: () => actionDef.handler(row),
2932
2958
  title: actionDef.title,
2933
- sx: { width: 22, minWidth: 22, height: 22, "& .MuiButton-startIcon": { m: 0 } },
2959
+ sx: { width: 22, height: 22, p: 0, color: "grey.800", "&:hover": { color: `${actionDef.color}.main` } },
2934
2960
  children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(actionDef.icon, { sx: { fontSize: 18 } })
2935
2961
  },
2936
2962
  actionDef.action
@@ -3340,20 +3366,36 @@ function campoDibujable(field) {
3340
3366
 
3341
3367
  // src/hooks/grid/useGridColumns.ts
3342
3368
  var estimateTextWidth = (text) => text.length * 8 + 16;
3343
- var calcularAnchosDeColumnas = (tableDefinition, filas) => {
3369
+ var contextoDeMedida;
3370
+ var anchoDeTitulo = (texto) => {
3371
+ if (contextoDeMedida === void 0) {
3372
+ try {
3373
+ contextoDeMedida = document.createElement("canvas").getContext("2d");
3374
+ } catch {
3375
+ contextoDeMedida = null;
3376
+ }
3377
+ }
3378
+ if (!contextoDeMedida) return texto.length * 9;
3379
+ contextoDeMedida.font = "bold 14.9px Roboto, Helvetica, Arial, sans-serif";
3380
+ return Math.ceil(contextoDeMedida.measureText(texto).width);
3381
+ };
3382
+ var EXTRA_TITULO = 8 + 20 + 4;
3383
+ var calcularAnchosDeColumnas = (tableDefinition, filas, anchoDisponible = Infinity) => {
3344
3384
  const anchos = /* @__PURE__ */ new Map();
3385
+ const anchosConTituloCompleto = /* @__PURE__ */ new Map();
3345
3386
  const muestra = filas.slice(0, 50);
3346
3387
  tableDefinition.fields.forEach((fieldDef) => {
3347
3388
  if (typeof fieldDef.width === "number" && fieldDef.width > 0) {
3348
3389
  anchos.set(fieldDef.name, fieldDef.width);
3390
+ anchosConTituloCompleto.set(fieldDef.name, fieldDef.width);
3349
3391
  return;
3350
3392
  }
3351
3393
  const headerLabel = fieldDef.label || fieldDef.name;
3352
3394
  const type = fieldDef.typeName?.toLowerCase() || "";
3353
- let typeMinWidth = 60;
3354
- if (type.includes("boolean") || type.includes("checkbox")) typeMinWidth = 60;
3355
- else if (type.includes("date") || type.includes("timestamp")) typeMinWidth = 110;
3356
- else if (type.includes("integer") || type.includes("numeric")) typeMinWidth = 80;
3395
+ let typeMinWidth = 80;
3396
+ if (type.includes("boolean") || type.includes("checkbox")) typeMinWidth = 80;
3397
+ else if (type.includes("date") || type.includes("timestamp")) typeMinWidth = 120;
3398
+ else if (type.includes("integer") || type.includes("numeric")) typeMinWidth = 90;
3357
3399
  const headerWidth = estimateTextWidth(headerLabel) + 20;
3358
3400
  const typer = typerFrom(fieldDef);
3359
3401
  const maxDataWidth = muestra.reduce((maxW, row) => {
@@ -3367,9 +3409,12 @@ var calcularAnchosDeColumnas = (tableDefinition, filas) => {
3367
3409
  }
3368
3410
  return Math.max(maxW, estimateTextWidth(texto));
3369
3411
  }, 0);
3370
- anchos.set(fieldDef.name, Math.min(Math.max(headerWidth, maxDataWidth, typeMinWidth), 500));
3412
+ const compacto = Math.min(Math.max(headerWidth, maxDataWidth, typeMinWidth), 500);
3413
+ anchos.set(fieldDef.name, compacto);
3414
+ anchosConTituloCompleto.set(fieldDef.name, Math.max(compacto, Math.min(anchoDeTitulo(cambiarGuionesBajosPorEspacios(headerLabel)) + EXTRA_TITULO, 500)));
3371
3415
  });
3372
- return anchos;
3416
+ const sumaCompleta = [...anchos.keys()].reduce((total, nombre) => total + (anchosConTituloCompleto.get(nombre) ?? 0), 0);
3417
+ return sumaCompleta <= anchoDisponible ? anchosConTituloCompleto : anchos;
3373
3418
  };
3374
3419
  var useGridColumns = ({
3375
3420
  tableDefinition,
@@ -4094,6 +4139,7 @@ var ALTO_TITULO_ANIDADA = 26;
4094
4139
  var ALTO_MINIMO_CUERPO_ANIDADA = 120;
4095
4140
  var HOLGURA_ANIDADA = 24;
4096
4141
  var ANCHO_MINIMO_ANIDADA = 240;
4142
+ var ANCHO_RESERVADO_PANTALLA = 320;
4097
4143
  var SANGRIA_DETALLE = 28 + 8 + 3;
4098
4144
  var MARGEN_INFERIOR_PAGINA = 24;
4099
4145
  var PRIMERAS_FILAS_ANIDADA = 20;
@@ -4309,7 +4355,7 @@ var GenericDataGrid = ({
4309
4355
  const firstDisplayCount = tableDefinition?.firstDisplayCount ?? PRIMERAS_FILAS_ANIDADA;
4310
4356
  const firstDisplayOverLimit = tableDefinition?.firstDisplayOverLimit ?? LIMITE_PRIMERAS_FILAS_ANIDADA;
4311
4357
  const filasVisiblesMax = anidada ? filteredRows.length <= firstDisplayOverLimit ? filteredRows.length : firstDisplayCount : void 0;
4312
- const conPie = !anidada || !!tableDefinition?.fields.some((field) => field.aggregate);
4358
+ const conPie = !!tableDefinition?.fields.some((field) => field.aggregate);
4313
4359
  const { gridHeight, bottomSummaryRow, summaryRows, filterHeight } = useGridLayout({
4314
4360
  filteredRows,
4315
4361
  isFilterRowVisible,
@@ -4403,7 +4449,7 @@ var GenericDataGrid = ({
4403
4449
  const definition = parametros.tableDef ? fusionar(definicionDelServidor, parametros.tableDef) : definicionDelServidor;
4404
4450
  setTableDefinition(definition);
4405
4451
  setSortColumns(getInitialSortColumns(definition));
4406
- setAnchosCalculados(calcularAnchosDeColumnas(definition, data));
4452
+ setAnchosCalculados(calcularAnchosDeColumnas(definition, data, window.innerWidth - ANCHO_RESERVADO_PANTALLA));
4407
4453
  setColumnasOcultas(columnasOcultasIniciales(definition));
4408
4454
  setTableData(data);
4409
4455
  const filasDeFiltros = parametros.filterColumns ?? aFiltrosIniciales(definition.filterColumns);
@@ -4604,7 +4650,7 @@ var GenericDataGrid = ({
4604
4650
  [exitingRowIds, primaryKey]
4605
4651
  );
4606
4652
  const anchoContenido = (0, import_react26.useMemo)(() => {
4607
- if (!anidada || columns.length === 0) return void 0;
4653
+ if (columns.length === 0) return void 0;
4608
4654
  const sumaColumnas = columns.reduce((total, col) => {
4609
4655
  const medido = columnWidths.get(col.key)?.width;
4610
4656
  return total + (typeof medido === "number" ? medido : typeof col.width === "number" ? col.width : 100);
@@ -4614,8 +4660,9 @@ var GenericDataGrid = ({
4614
4660
  return Math.max(ANCHO_MINIMO_ANIDADA, propio, anchoDetalleMax > 0 ? anchoDetalleMax + SANGRIA_DETALLE + (medidas?.barra ?? 0) + 2 : 0);
4615
4661
  }, [anidada, columns, columnWidths, medidas?.barra, detallesAbiertos, anchosDetalle]);
4616
4662
  (0, import_react26.useEffect)(() => {
4617
- if (anchoContenido !== void 0) onAnchoDeseado?.(anchoContenido);
4618
- }, [onAnchoDeseado, anchoContenido]);
4663
+ if (anidada && anchoContenido !== void 0) onAnchoDeseado?.(anchoContenido);
4664
+ }, [onAnchoDeseado, anidada, anchoContenido]);
4665
+ const anchoExterior = anchoContenido === void 0 ? void 0 : anidada ? anchoContenido : anchoContenido - 2 + 32 + ancestors.length * 32;
4619
4666
  const estiloGrilla = (0, import_react26.useMemo)(() => ({ height: "100%", width: "100%", boxSizing: "border-box", ...gridStyles }), [gridStyles]);
4620
4667
  const bottomSummaryRows = (0, import_react26.useMemo)(() => conPie ? [bottomSummaryRow] : [], [conPie, bottomSummaryRow]);
4621
4668
  const vistaGrilla = (0, import_react26.useMemo)(() => ({ isFilterRowVisible }), [isFilterRowVisible]);
@@ -4641,7 +4688,7 @@ var GenericDataGrid = ({
4641
4688
  if (!tableDefinition) {
4642
4689
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_material19.Box, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "200px", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_material19.Alert, { severity: "warning", children: "No se pudo cargar la definici\xF3n de la tabla." }) });
4643
4690
  }
4644
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CacheGrillasContext.Provider, { value: cache, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_material19.Box, { sx: { pt: anidada ? 0 : 2, display: "flex", flexDirection: "column", height: "100%", boxSizing: "border-box", width: anchoContenido ?? void 0, maxWidth: "100%" }, children: [
4691
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CacheGrillasContext.Provider, { value: cache, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_material19.Box, { sx: { pt: anidada ? 0 : 2, display: "flex", flexDirection: "column", height: "100%", boxSizing: "border-box", width: anchoExterior, maxWidth: "100%" }, children: [
4645
4692
  !anidada && ancestors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_material19.Box, { sx: { px: 2, pt: 0, pb: 1, display: "flex", flexDirection: "column" }, children: ancestors.map((ancestor, index) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4646
4693
  StaticAncestorGrid,
4647
4694
  {
@@ -4738,7 +4785,7 @@ var GenericDataGrid = ({
4738
4785
  sx: {
4739
4786
  position: "absolute",
4740
4787
  top: filterHeight + HEADER_ROW_HEIGHT + 1,
4741
- bottom: anidada ? (conPie ? SUMMARY_ROW_HEIGHT : 0) + 4 : 102,
4788
+ bottom: anidada ? (conPie ? SUMMARY_ROW_HEIGHT : 0) + 4 : conPie ? 102 : 102 - SUMMARY_ROW_HEIGHT,
4742
4789
  left: 18,
4743
4790
  right: 0,
4744
4791
  display: "flex",
@@ -4863,8 +4910,8 @@ var GenericDataGrid = ({
4863
4910
  var GenericDataGrid_default = GenericDataGrid;
4864
4911
 
4865
4912
  // src/App.tsx
4866
- var import_react43 = __toESM(require("react"), 1);
4867
- var import_react_router_dom15 = require("react-router-dom");
4913
+ var import_react44 = __toESM(require("react"), 1);
4914
+ var import_react_router_dom18 = require("react-router-dom");
4868
4915
 
4869
4916
  // src/components/MainLayout.tsx
4870
4917
  var import_react30 = require("react");
@@ -4875,6 +4922,15 @@ var import_material21 = require("@mui/material");
4875
4922
  var import_react27 = require("react");
4876
4923
  var import_react_router_dom = require("react-router-dom");
4877
4924
  var import_react_redux2 = require("react-redux");
4925
+
4926
+ // src/utils/cierreVoluntario.ts
4927
+ var cierreVoluntario = false;
4928
+ var marcarCierreVoluntario = (activo) => {
4929
+ cierreVoluntario = activo;
4930
+ };
4931
+ var huboCierreVoluntario = () => cierreVoluntario;
4932
+
4933
+ // src/contexts/AppContext.tsx
4878
4934
  var import_jsx_runtime24 = require("react/jsx-runtime");
4879
4935
  var AuthContext = (0, import_react27.createContext)(void 0);
4880
4936
  var useApp = () => {
@@ -4891,9 +4947,10 @@ var AppProvider = ({ children, publicPaths = [] }) => {
4891
4947
  const [showSessionExpiredMessage, setShowSessionExpiredMessage] = (0, import_react27.useState)(false);
4892
4948
  const isRedirectingRef = (0, import_react27.useRef)(false);
4893
4949
  const huboSesionRef = (0, import_react27.useRef)(false);
4950
+ const cierreVoluntarioRef = (0, import_react27.useRef)(false);
4894
4951
  const dispatch = (0, import_react_redux2.useDispatch)();
4895
4952
  const clientContext = (0, import_react_redux2.useSelector)((state) => state.clientContext);
4896
- if (clientContext.status === "succeeded") huboSesionRef.current = true;
4953
+ if (clientContext.status === "succeeded" && !cierreVoluntarioRef.current) huboSesionRef.current = true;
4897
4954
  const navigate = (0, import_react_router_dom.useNavigate)();
4898
4955
  const handleExpiredSessionRedirect = (0, import_react27.useCallback)(() => {
4899
4956
  isRedirectingRef.current = true;
@@ -4904,6 +4961,17 @@ var AppProvider = ({ children, publicPaths = [] }) => {
4904
4961
  isRedirectingRef.current = false;
4905
4962
  }, 2e3);
4906
4963
  }, [navigate]);
4964
+ const avisarCierreVoluntario = (0, import_react27.useCallback)((activo) => {
4965
+ cierreVoluntarioRef.current = activo;
4966
+ marcarCierreVoluntario(activo);
4967
+ if (activo) huboSesionRef.current = false;
4968
+ }, []);
4969
+ const limpiarContextoPorSesionPerdida = (0, import_react27.useCallback)(() => {
4970
+ if (clientContext.status === "succeeded" && clientContext.username !== "" && !cierreVoluntarioRef.current) {
4971
+ dispatch(setRedirectUsername(clientContext.username));
4972
+ }
4973
+ dispatch(clearClientContext());
4974
+ }, [dispatch, clientContext.status, clientContext.username]);
4907
4975
  const location = (0, import_react_router_dom.useLocation)();
4908
4976
  const isCurrentPathPublic = (0, import_react27.useCallback)(() => {
4909
4977
  const current = location.pathname;
@@ -4929,8 +4997,8 @@ var AppProvider = ({ children, publicPaths = [] }) => {
4929
4997
  }
4930
4998
  } else {
4931
4999
  setIsLoggedIn(false);
4932
- dispatch(clearClientContext());
4933
- if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current) {
5000
+ limpiarContextoPorSesionPerdida();
5001
+ if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current && !cierreVoluntarioRef.current) {
4934
5002
  console.warn("Sesi\xF3n inv\xE1lida en ruta protegida. Mostrando modal.");
4935
5003
  setShowSessionExpiredMessage(true);
4936
5004
  } else {
@@ -4940,13 +5008,13 @@ var AppProvider = ({ children, publicPaths = [] }) => {
4940
5008
  } catch (error) {
4941
5009
  console.error("Error checking session:", error);
4942
5010
  setIsLoggedIn(false);
4943
- dispatch(clearClientContext());
5011
+ limpiarContextoPorSesionPerdida();
4944
5012
  const isPublic = publicPaths.some((p) => location.pathname === p || location.pathname.startsWith(p + "/"));
4945
- if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current) {
5013
+ if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current && !cierreVoluntarioRef.current) {
4946
5014
  setShowSessionExpiredMessage(true);
4947
5015
  }
4948
5016
  }
4949
- }, [dispatch, clientContext.status, showSessionExpiredMessage, isCurrentPathPublic]);
5017
+ }, [dispatch, clientContext.status, showSessionExpiredMessage, isCurrentPathPublic, limpiarContextoPorSesionPerdida]);
4950
5018
  (0, import_react27.useEffect)(() => {
4951
5019
  checkSession();
4952
5020
  const intervalId = setInterval(checkSession, 15 * 1e3);
@@ -4960,20 +5028,24 @@ var AppProvider = ({ children, publicPaths = [] }) => {
4960
5028
  checkSession,
4961
5029
  showSessionExpiredMessage,
4962
5030
  setShowSessionExpiredMessage,
4963
- handleExpiredSessionRedirect
5031
+ handleExpiredSessionRedirect,
5032
+ avisarCierreVoluntario
4964
5033
  }), [
4965
5034
  isLoggedIn,
4966
5035
  setIsLoggedIn,
4967
5036
  checkSession,
4968
5037
  showSessionExpiredMessage,
4969
5038
  setShowSessionExpiredMessage,
4970
- handleExpiredSessionRedirect
5039
+ handleExpiredSessionRedirect,
5040
+ avisarCierreVoluntario
4971
5041
  ]);
4972
5042
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AuthContext.Provider, { value: authContextValue, children });
4973
5043
  };
4974
5044
 
4975
5045
  // src/components/MainLayout.tsx
5046
+ var import_react_router_dom5 = require("react-router-dom");
4976
5047
  var import_Logout = __toESM(require("@mui/icons-material/Logout"), 1);
5048
+ var import_VpnKey = __toESM(require("@mui/icons-material/VpnKey"), 1);
4977
5049
  var import_Menu = __toESM(require("@mui/icons-material/Menu"), 1);
4978
5050
  var import_ChevronLeft = __toESM(require("@mui/icons-material/ChevronLeft"), 1);
4979
5051
 
@@ -4987,11 +5059,18 @@ var import_Folder = __toESM(require("@mui/icons-material/Folder"), 1);
4987
5059
  var import_Dns = __toESM(require("@mui/icons-material/Dns"), 1);
4988
5060
  var import_WarningAmber3 = __toESM(require("@mui/icons-material/WarningAmber"), 1);
4989
5061
  var import_DesktopWindows = __toESM(require("@mui/icons-material/DesktopWindows"), 1);
4990
- var import_UnfoldMore = __toESM(require("@mui/icons-material/UnfoldMore"), 1);
4991
- var import_UnfoldLess = __toESM(require("@mui/icons-material/UnfoldLess"), 1);
5062
+ var import_Search3 = __toESM(require("@mui/icons-material/Search"), 1);
5063
+ var import_Clear = __toESM(require("@mui/icons-material/Clear"), 1);
4992
5064
  var import_react_router_dom2 = require("react-router-dom");
4993
5065
  var import_colors = require("@mui/material/colors");
4994
5066
 
5067
+ // src/puente/habilitacion.ts
5068
+ var habilitado = false;
5069
+ var habilitarPuenteBP = () => {
5070
+ habilitado = true;
5071
+ };
5072
+ var puenteBPHabilitado = () => habilitado;
5073
+
4995
5074
  // src/utils/menuInicial.ts
4996
5075
  function rutaDeTablaDeMenu(camino) {
4997
5076
  return `/menu/${camino.map(encodeURIComponent).join("/")}`;
@@ -5042,16 +5121,93 @@ function itemInicial(menu) {
5042
5121
  return elegido.menuType === "menu" ? itemInicial(elegido.menuContent) : elegido;
5043
5122
  }
5044
5123
 
5124
+ // src/utils/busquedaDeMenu.ts
5125
+ function etiquetaDeItem(item) {
5126
+ return item.label || item.name;
5127
+ }
5128
+ function claveDeMenu(camino) {
5129
+ return camino.join("/");
5130
+ }
5131
+ function hojasDeMenu(menu, grupos = [], etiquetasDeGrupos = []) {
5132
+ return menu.flatMap(
5133
+ (item) => item.menuType === "menu" ? hojasDeMenu(item.menuContent, [...grupos, item.name], [...etiquetasDeGrupos, etiquetaDeItem(item)]) : [{ item, grupos, etiquetasDeGrupos, clave: claveDeMenu([...grupos, item.name]) }]
5134
+ );
5135
+ }
5136
+ function normalizarParaBuscar(texto) {
5137
+ return texto.normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/_/g, " ").toLowerCase();
5138
+ }
5139
+ function buscarEnMenu(hojas, consulta) {
5140
+ const palabras = normalizarParaBuscar(consulta).split(/\s+/).filter((palabra) => palabra !== "");
5141
+ if (palabras.length === 0) return [];
5142
+ const propias = [];
5143
+ const porGrupo = [];
5144
+ for (const hoja of hojas) {
5145
+ const propia = normalizarParaBuscar(`${etiquetaDeItem(hoja.item)} ${hoja.item.name}`);
5146
+ const completa = `${hoja.etiquetasDeGrupos.map(normalizarParaBuscar).join(" ")} ${propia}`;
5147
+ if (!palabras.every((palabra) => completa.includes(palabra))) continue;
5148
+ (palabras.every((palabra) => propia.includes(palabra)) ? propias : porGrupo).push(hoja);
5149
+ }
5150
+ return [...propias, ...porGrupo];
5151
+ }
5152
+
5045
5153
  // src/components/SideMenu.tsx
5046
5154
  var import_jsx_runtime25 = require("react/jsx-runtime");
5047
- var MenuListItem = ({ item, level, grupos = [], onMenuItemClick }) => {
5155
+ var CLAVE_RECIENTES = "fpr.menu.recientes";
5156
+ var MAXIMO_RECIENTES = 4;
5157
+ var getIcon = (item, destacado = false) => {
5158
+ switch (item.menuType) {
5159
+ case "table":
5160
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_TableChart.default, { sx: destacado ? { color: import_colors.blue[700] } : void 0 });
5161
+ case "proc":
5162
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_Dns.default, { sx: destacado ? { color: import_colors.teal[700] } : void 0 });
5163
+ case "menu":
5164
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_Folder.default, { sx: destacado ? { color: import_colors.orange[800] } : void 0 });
5165
+ default:
5166
+ if (wScreens[item.menuType] !== void 0 || puenteBPHabilitado()) {
5167
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_DesktopWindows.default, { sx: destacado ? { color: import_colors.indigo[500] } : void 0 });
5168
+ }
5169
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_WarningAmber3.default, { sx: destacado ? { color: import_colors.red[500] } : void 0 });
5170
+ }
5171
+ };
5172
+ var estaSeleccionado = (item, grupos, currentPath, hash = "") => {
5173
+ const pathParts = currentPath.split("/");
5174
+ const urlTableName = pathParts[1] === "table" ? pathParts[2] : null;
5175
+ if (item.menuType === "table") {
5176
+ return currentPath === rutaDeTablaDeMenu([...grupos, item.name]) || urlTableName === (item.table || item.name);
5177
+ } else if (item.menuType === "proc") {
5178
+ return currentPath === `/procedures/${item.name}`;
5179
+ } else if (item.menuType !== "menu") {
5180
+ return currentPath.startsWith(`/wScreens/${item.menuType}`) || currentPath.startsWith(`/wScreens-fallback/${item.menuType}`) || currentPath === "/bp/menu" && new URLSearchParams(hash.replace(/^[#?]/, "")).get("w") === item.menuType;
5181
+ }
5182
+ return false;
5183
+ };
5184
+ var leerRecientes = () => {
5185
+ try {
5186
+ const guardado = JSON.parse(localStorage.getItem(CLAVE_RECIENTES) ?? "[]");
5187
+ return Array.isArray(guardado) ? guardado.filter((clave) => typeof clave === "string") : [];
5188
+ } catch {
5189
+ return [];
5190
+ }
5191
+ };
5192
+ var guardarRecientes = (recientes) => {
5193
+ try {
5194
+ localStorage.setItem(CLAVE_RECIENTES, JSON.stringify(recientes));
5195
+ } catch {
5196
+ }
5197
+ };
5198
+ var MenuListItem = ({ item, level, grupos = [], hermanos = [], onMenuItemClick }) => {
5048
5199
  const navigate = (0, import_react_router_dom2.useNavigate)();
5049
5200
  const location = (0, import_react_router_dom2.useLocation)();
5050
- const open = useSubMenuOpenState(item.name);
5201
+ const clave = claveDeMenu([...grupos, item.name]);
5202
+ const open = useSubMenuOpenState(clave);
5051
5203
  const dispatch = useAppDispatch();
5052
5204
  const handleClick = () => {
5053
5205
  if (item.menuType === "menu") {
5054
- dispatch(toggleSubMenu(item.name));
5206
+ if (open) {
5207
+ dispatch(setSubMenuOpen({ menuName: clave, isOpen: false }));
5208
+ } else {
5209
+ dispatch(abrirGrupo({ clave, hermanos }));
5210
+ }
5055
5211
  } else {
5056
5212
  navigate(rutaDeItemDeMenu(item, grupos));
5057
5213
  if (onMenuItemClick) {
@@ -5059,39 +5215,14 @@ var MenuListItem = ({ item, level, grupos = [], onMenuItemClick }) => {
5059
5215
  }
5060
5216
  }
5061
5217
  };
5062
- const getIcon = (item2) => {
5063
- switch (item2.menuType) {
5064
- case "table":
5065
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_TableChart.default, { sx: { color: import_colors.blue[700] } });
5066
- case "proc":
5067
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_Dns.default, { sx: { color: import_colors.teal[700] } });
5068
- case "menu":
5069
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_Folder.default, { sx: { color: import_colors.orange[800] } });
5070
- default:
5071
- const WScreenComponent = wScreens[item2.menuType];
5072
- if (WScreenComponent !== void 0) {
5073
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_DesktopWindows.default, { sx: { color: import_colors.grey[700] } });
5074
- }
5075
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_WarningAmber3.default, { sx: { color: import_colors.red[500] } });
5076
- }
5077
- };
5078
- const currentPath = location.pathname;
5079
- let isSelected = false;
5080
- const pathParts = currentPath.split("/");
5081
- const urlTableName = pathParts[1] === "table" ? pathParts[2] : null;
5082
- if (item.menuType === "table") {
5083
- isSelected = currentPath === rutaDeTablaDeMenu([...grupos, item.name]) || urlTableName === (item.table || item.name);
5084
- } else if (item.menuType === "proc") {
5085
- isSelected = currentPath === `/procedures/${item.name}`;
5086
- } else if (item.menuType !== "menu") {
5087
- isSelected = currentPath.startsWith(`/wScreens/${item.menuType}`) || currentPath.startsWith(`/wScreens-fallback/${item.menuType}`);
5088
- }
5218
+ const isSelected = estaSeleccionado(item, grupos, location.pathname, location.hash);
5089
5219
  const itemRef = import_react28.default.useRef(null);
5090
5220
  (0, import_react28.useEffect)(() => {
5091
5221
  if (isSelected && itemRef.current) {
5092
5222
  itemRef.current.scrollIntoView({ behavior: "smooth", block: "nearest" });
5093
5223
  }
5094
5224
  }, [isSelected]);
5225
+ const contenido = item.menuType === "menu" ? item.menuContent : [];
5095
5226
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { ref: itemRef, children: [
5096
5227
  /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
5097
5228
  import_material20.ListItemButton,
@@ -5100,83 +5231,156 @@ var MenuListItem = ({ item, level, grupos = [], onMenuItemClick }) => {
5100
5231
  sx: { pl: level * 2 },
5101
5232
  selected: isSelected,
5102
5233
  children: [
5103
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.ListItemIcon, { sx: { minWidth: "38px" }, children: getIcon(item) }),
5104
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.ListItemText, { primary: item.label || item.name }),
5234
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.ListItemIcon, { sx: { minWidth: "38px" }, children: getIcon(item, isSelected || open) }),
5235
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.ListItemText, { primary: etiquetaDeItem(item) }),
5105
5236
  item.menuType === "menu" ? open ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ExpandLess.default, {}) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ExpandMore.default, {}) : null
5106
5237
  ]
5107
5238
  }
5108
5239
  ),
5109
- item.menuType === "menu" && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Collapse, { in: open, timeout: "auto", unmountOnExit: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.List, { component: "div", disablePadding: true, children: item.menuContent.map((subItem) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5240
+ item.menuType === "menu" && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Collapse, { in: open, timeout: "auto", unmountOnExit: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.List, { component: "div", disablePadding: true, children: contenido.map((subItem) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5110
5241
  MenuListItem,
5111
5242
  {
5112
5243
  item: subItem,
5113
5244
  level: level + 1,
5114
5245
  grupos: [...grupos, item.name],
5246
+ hermanos: contenido.filter((otro) => otro !== subItem).map((otro) => claveDeMenu([...grupos, item.name, otro.name])),
5115
5247
  onMenuItemClick
5116
5248
  },
5117
5249
  subItem.name
5118
5250
  )) }) })
5119
5251
  ] });
5120
5252
  };
5253
+ var ItemPlano = ({ hoja, seleccionado, traerALaVista = false, onElegir }) => {
5254
+ const boton = (0, import_react28.useRef)(null);
5255
+ (0, import_react28.useEffect)(() => {
5256
+ if (traerALaVista && seleccionado) boton.current?.scrollIntoView({ block: "nearest" });
5257
+ }, [traerALaVista, seleccionado]);
5258
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_material20.ListItemButton, { dense: true, selected: seleccionado, onClick: () => onElegir(hoja), ref: boton, children: [
5259
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.ListItemIcon, { sx: { minWidth: "38px" }, children: getIcon(hoja.item, seleccionado) }),
5260
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5261
+ import_material20.ListItemText,
5262
+ {
5263
+ primary: etiquetaDeItem(hoja.item),
5264
+ secondary: hoja.etiquetasDeGrupos.join(" \u203A ") || void 0,
5265
+ secondaryTypographyProps: { noWrap: true }
5266
+ }
5267
+ )
5268
+ ] });
5269
+ };
5121
5270
  var SideMenu = ({ onMenuItemClick }) => {
5122
5271
  const { clientContext } = useApp();
5123
5272
  const navigate = (0, import_react_router_dom2.useNavigate)();
5124
5273
  const location = (0, import_react_router_dom2.useLocation)();
5125
5274
  const dispatch = useAppDispatch();
5126
- const subMenuOpenStates = useAppSelector((state) => state.menuUi.subMenuOpenStates);
5127
- const allSubMenusKeys = Object.keys(subMenuOpenStates);
5128
- const openSubMenusCount = allSubMenusKeys.filter((key) => subMenuOpenStates[key]).length;
5129
- const isMostlyOpen = allSubMenusKeys.length > 0 && openSubMenusCount / allSubMenusKeys.length > 0.5;
5130
- const handleToggleAllSubMenus = () => {
5131
- dispatch(setAllSubMenusOpen(!isMostlyOpen));
5132
- };
5133
- if (!clientContext || !clientContext.menu) {
5275
+ const [consulta, setConsulta] = (0, import_react28.useState)("");
5276
+ const [indice, setIndice] = (0, import_react28.useState)(0);
5277
+ const [recientes, setRecientes] = (0, import_react28.useState)(leerRecientes);
5278
+ const campoDeBusqueda = (0, import_react28.useRef)(null);
5279
+ const menu = clientContext?.menu;
5280
+ const hojas = (0, import_react28.useMemo)(() => hojasDeMenu(menu ?? []), [menu]);
5281
+ const resultados = (0, import_react28.useMemo)(() => buscarEnMenu(hojas, consulta), [hojas, consulta]);
5282
+ (0, import_react28.useEffect)(() => {
5283
+ const alApretar = (evento) => {
5284
+ if ((evento.ctrlKey || evento.metaKey) && !evento.altKey && !evento.shiftKey && evento.key.toLowerCase() === "k") {
5285
+ evento.preventDefault();
5286
+ dispatch(setDrawerOpen(true));
5287
+ setTimeout(() => {
5288
+ campoDeBusqueda.current?.focus();
5289
+ campoDeBusqueda.current?.select();
5290
+ }, 50);
5291
+ }
5292
+ };
5293
+ window.addEventListener("keydown", alApretar);
5294
+ return () => window.removeEventListener("keydown", alApretar);
5295
+ }, [dispatch]);
5296
+ (0, import_react28.useEffect)(() => {
5297
+ const actual = hojas.find((hoja) => estaSeleccionado(hoja.item, hoja.grupos, location.pathname, location.hash));
5298
+ if (!actual) return;
5299
+ dispatch(abrirCamino(actual.grupos.map((_, i) => claveDeMenu(actual.grupos.slice(0, i + 1)))));
5300
+ setRecientes((anteriores) => {
5301
+ if (anteriores.includes(actual.clave)) return anteriores;
5302
+ const nuevos = [actual.clave, ...anteriores].slice(0, MAXIMO_RECIENTES);
5303
+ guardarRecientes(nuevos);
5304
+ return nuevos;
5305
+ });
5306
+ }, [location.pathname, location.hash, hojas, dispatch]);
5307
+ if (!menu) {
5134
5308
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Box, { sx: { p: 2, textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Typography, { variant: "body2", color: "text.secondary", children: "Cargando men\xFA..." }) });
5135
5309
  }
5136
- (0, import_react28.useEffect)(() => {
5137
- if (!clientContext?.menu) return;
5138
- const expandParents = (items, targetPath, parentNames = []) => {
5139
- const pathParts = targetPath.split("/");
5140
- const urlTableName = pathParts[1] === "table" ? pathParts[2] : null;
5141
- for (const item of items) {
5142
- let matches = false;
5143
- if (item.menuType === "table") {
5144
- matches = targetPath === rutaDeTablaDeMenu([...parentNames, item.name]) || urlTableName === (item.table || item.name);
5145
- } else if (item.menuType === "proc") {
5146
- matches = targetPath === `/procedures/${item.name}`;
5147
- } else if (item.menuType !== "menu") {
5148
- matches = targetPath.startsWith(`/wScreens/${item.menuType}`) || targetPath.startsWith(`/wScreens-fallback/${item.menuType}`);
5149
- }
5150
- if (matches) {
5151
- parentNames.forEach((name) => {
5152
- dispatch(setSubMenuOpen({ menuName: name, isOpen: true }));
5153
- });
5154
- return true;
5155
- }
5156
- if (item.menuType === "menu") {
5157
- const found = expandParents(item.menuContent, targetPath, [...parentNames, item.name]);
5158
- if (found) return true;
5310
+ const elegir = (hoja) => {
5311
+ navigate(rutaDeItemDeMenu(hoja.item, hoja.grupos));
5312
+ setConsulta("");
5313
+ setIndice(0);
5314
+ if (onMenuItemClick) {
5315
+ onMenuItemClick();
5316
+ }
5317
+ };
5318
+ const alApretarEnBusqueda = (evento) => {
5319
+ if (evento.key === "ArrowDown" || evento.key === "ArrowUp") {
5320
+ evento.preventDefault();
5321
+ if (resultados.length === 0) return;
5322
+ setIndice((actual) => (actual + (evento.key === "ArrowDown" ? 1 : -1) + resultados.length) % resultados.length);
5323
+ } else if (evento.key === "Enter") {
5324
+ evento.preventDefault();
5325
+ const hoja = resultados[indice];
5326
+ if (hoja) elegir(hoja);
5327
+ } else if (evento.key === "Escape" && consulta !== "") {
5328
+ evento.stopPropagation();
5329
+ setConsulta("");
5330
+ }
5331
+ };
5332
+ const hojasRecientes = recientes.map((clave) => hojas.find((hoja) => hoja.clave === clave)).filter((hoja) => hoja !== void 0);
5333
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_material20.Box, { sx: { width: "100%", flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }, children: [
5334
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Box, { sx: { px: 1.5, py: 1, flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5335
+ import_material20.TextField,
5336
+ {
5337
+ inputRef: campoDeBusqueda,
5338
+ value: consulta,
5339
+ onChange: (evento) => {
5340
+ setConsulta(evento.target.value);
5341
+ setIndice(0);
5342
+ },
5343
+ onKeyDown: alApretarEnBusqueda,
5344
+ placeholder: "Buscar (Ctrl+K)",
5345
+ size: "small",
5346
+ fullWidth: true,
5347
+ autoComplete: "off",
5348
+ sx: { "& .MuiInputBase-input": { fontSize: "0.8rem" } },
5349
+ InputProps: {
5350
+ startAdornment: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.InputAdornment, { position: "start", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_Search3.default, { fontSize: "small" }) }),
5351
+ endAdornment: consulta !== "" ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.InputAdornment, { position: "end", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.IconButton, { size: "small", "aria-label": "borrar b\xFAsqueda", edge: "end", onClick: () => {
5352
+ setConsulta("");
5353
+ campoDeBusqueda.current?.focus();
5354
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_Clear.default, { fontSize: "small" }) }) }) : void 0
5159
5355
  }
5160
5356
  }
5161
- return false;
5162
- };
5163
- expandParents(clientContext.menu, location.pathname);
5164
- }, [location.pathname, clientContext, dispatch]);
5165
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_material20.Box, { sx: { width: "100%", flexShrink: 0 }, children: [
5166
- clientContext.menu.some((item) => item.menuType === "menu") && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.ListItem, { disablePadding: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_material20.ListItemButton, { onClick: handleToggleAllSubMenus, children: [
5167
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.ListItemIcon, { sx: { minWidth: "38px" }, children: isMostlyOpen ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_UnfoldLess.default, {}) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_UnfoldMore.default, {}) }),
5168
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.ListItemText, { primary: isMostlyOpen ? "colapsar " : "expandir" })
5169
- ] }) }),
5357
+ ) }),
5170
5358
  /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Divider, {}),
5171
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.List, { children: clientContext.menu.map((menuItem) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5172
- MenuListItem,
5173
- {
5174
- item: menuItem,
5175
- level: 1,
5176
- onMenuItemClick
5177
- },
5178
- menuItem.name
5179
- )) })
5359
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Box, { sx: { flex: 1, minHeight: 0, overflowY: "auto" }, children: consulta.trim() !== "" ? resultados.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Box, { sx: { p: 2, textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Typography, { variant: "body2", color: "text.secondary", children: "Sin resultados" }) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.List, { children: resultados.map((hoja, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemPlano, { hoja, seleccionado: i === indice, traerALaVista: true, onElegir: elegir }, hoja.clave)) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
5360
+ hojasRecientes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
5361
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Typography, { variant: "overline", color: "text.secondary", sx: { display: "block", px: 2, pt: 1, lineHeight: 2 }, children: "Recientes" }),
5362
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.List, { disablePadding: true, children: hojasRecientes.map((hoja) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5363
+ ItemPlano,
5364
+ {
5365
+ hoja,
5366
+ seleccionado: estaSeleccionado(hoja.item, hoja.grupos, location.pathname, location.hash),
5367
+ onElegir: elegir
5368
+ },
5369
+ hoja.clave
5370
+ )) }),
5371
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Divider, {})
5372
+ ] }),
5373
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.List, { children: menu.map((menuItem) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5374
+ MenuListItem,
5375
+ {
5376
+ item: menuItem,
5377
+ level: 1,
5378
+ hermanos: menu.filter((otro) => otro !== menuItem).map((otro) => claveDeMenu([otro.name])),
5379
+ onMenuItemClick
5380
+ },
5381
+ menuItem.name
5382
+ )) })
5383
+ ] }) })
5180
5384
  ] });
5181
5385
  };
5182
5386
  var SideMenu_default = SideMenu;
@@ -5186,10 +5390,11 @@ var import_react29 = require("react");
5186
5390
  var import_react_router_dom3 = require("react-router-dom");
5187
5391
  var import_react_redux3 = require("react-redux");
5188
5392
  var useLogout = () => {
5189
- const { setIsLoggedIn } = useApp();
5393
+ const { setIsLoggedIn, avisarCierreVoluntario } = useApp();
5190
5394
  const dispatch = (0, import_react_redux3.useDispatch)();
5191
5395
  const navigate = (0, import_react_router_dom3.useNavigate)();
5192
5396
  const logout = (0, import_react29.useCallback)(async () => {
5397
+ avisarCierreVoluntario(true);
5193
5398
  try {
5194
5399
  const response = await fetchApi("/logout", { method: "GET" });
5195
5400
  if (response.status === 401 || response.ok) {
@@ -5202,9 +5407,10 @@ var useLogout = () => {
5202
5407
  } finally {
5203
5408
  setIsLoggedIn(false);
5204
5409
  dispatch(clearClientContext());
5410
+ dispatch(setRedirectPath(null));
5205
5411
  navigate("/login", { replace: true });
5206
5412
  }
5207
- }, [setIsLoggedIn, dispatch, navigate]);
5413
+ }, [setIsLoggedIn, avisarCierreVoluntario, dispatch, navigate]);
5208
5414
  return logout;
5209
5415
  };
5210
5416
  var useLogout_default = useLogout;
@@ -5215,6 +5421,7 @@ var drawerWidth = 240;
5215
5421
  var MainLayout = () => {
5216
5422
  const { clientContext } = useApp();
5217
5423
  const logout = useLogout_default();
5424
+ const navigate = (0, import_react_router_dom5.useNavigate)();
5218
5425
  const isDrawerOpen = useIsDrawerOpen();
5219
5426
  const dispatch = useAppDispatch();
5220
5427
  const theme = (0, import_material21.useTheme)();
@@ -5280,6 +5487,7 @@ var MainLayout = () => {
5280
5487
  ),
5281
5488
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_material21.Typography, { variant: "h6", noWrap: true, component: "div", sx: { flexGrow: 1 }, children: isDrawerOpen ? "" : clientContext.config.title }),
5282
5489
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_material21.Typography, { variant: "subtitle1", component: "div", sx: { mr: 2 }, children: clientContext.username }),
5490
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_material21.IconButton, { color: "inherit", title: "Cambiar contrase\xF1a", "aria-label": "cambiar contrase\xF1a", onClick: () => navigate("/chpass"), sx: { mr: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_VpnKey.default, {}) }),
5283
5491
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5284
5492
  import_material21.Button,
5285
5493
  {
@@ -5304,8 +5512,9 @@ var MainLayout = () => {
5304
5512
  "& .MuiDrawer-paper": {
5305
5513
  width: drawerWidth,
5306
5514
  boxSizing: "border-box",
5307
- overflowX: "hidden",
5308
- overflowY: "auto"
5515
+ overflow: "hidden",
5516
+ display: "flex",
5517
+ flexDirection: "column"
5309
5518
  }
5310
5519
  },
5311
5520
  children: [
@@ -5317,6 +5526,8 @@ var MainLayout = () => {
5317
5526
  alignItems: "center",
5318
5527
  justifyContent: "flex-start",
5319
5528
  px: 1,
5529
+ flexShrink: 0,
5530
+ // el logo queda fijo: solo scrollea la lista del menú
5320
5531
  fontSize: "1.3rem",
5321
5532
  minHeight: theme.mixins.toolbar.minHeight,
5322
5533
  gap: 1.5
@@ -5426,12 +5637,12 @@ var SessionExpiredMessage = () => {
5426
5637
  var SessionExpiredMessage_default = SessionExpiredMessage;
5427
5638
 
5428
5639
  // src/pages/PantallaInicial.tsx
5429
- var import_react_router_dom5 = require("react-router-dom");
5640
+ var import_react_router_dom6 = require("react-router-dom");
5430
5641
  var import_jsx_runtime28 = require("react/jsx-runtime");
5431
5642
  var PantallaInicial = () => {
5432
5643
  const { clientContext } = useApp();
5433
5644
  const item = clientContext.menu ? itemInicial(clientContext.menu) : void 0;
5434
- return item && clientContext.menu ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_router_dom5.Navigate, { to: rutaDeItemDeMenu(item, gruposDeItem(clientContext.menu, item)), replace: true }) : null;
5645
+ return item && clientContext.menu ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_router_dom6.Navigate, { to: rutaDeItemDeMenu(item, gruposDeItem(clientContext.menu, item)), replace: true }) : null;
5435
5646
  };
5436
5647
  var PantallaInicial_default = PantallaInicial;
5437
5648
 
@@ -5461,6 +5672,17 @@ var LogoutPage_default = LogoutPage;
5461
5672
  // src/pages/LoginPage.tsx
5462
5673
  var import_react32 = require("react");
5463
5674
  var import_material24 = require("@mui/material");
5675
+ var import_react_router_dom7 = require("react-router-dom");
5676
+
5677
+ // src/pages/configuracionLogin.ts
5678
+ var RUTA_POR_OMISION = "/new-pass";
5679
+ var rutaDeRecuperarClave = null;
5680
+ var configurarRecuperarClave = (recuperarClave) => {
5681
+ rutaDeRecuperarClave = recuperarClave === true ? RUTA_POR_OMISION : typeof recuperarClave === "string" && recuperarClave !== "" ? recuperarClave : null;
5682
+ };
5683
+ var rutaParaRecuperarClave = () => rutaDeRecuperarClave;
5684
+
5685
+ // src/pages/LoginPage.tsx
5464
5686
  var import_react_hook_form = require("react-hook-form");
5465
5687
  var import_zod = require("@hookform/resolvers/zod");
5466
5688
  var z = __toESM(require("zod"), 1);
@@ -5475,7 +5697,10 @@ var loginSchema = z.object({
5475
5697
  var LoginPage = () => {
5476
5698
  const [loginError, setLoginError] = (0, import_react32.useState)(null);
5477
5699
  const [isSubmitting, setIsSubmitting] = (0, import_react32.useState)(false);
5478
- const { setIsLoggedIn, checkSession } = useApp();
5700
+ const estadoDeLaRuta = (0, import_react_router_dom7.useLocation)().state;
5701
+ const claveCambiada = typeof estadoDeLaRuta === "object" && estadoDeLaRuta !== null && "claveCambiada" in estadoDeLaRuta && estadoDeLaRuta.claveCambiada === true;
5702
+ const rutaDeRecuperarClave2 = rutaParaRecuperarClave();
5703
+ const { setIsLoggedIn, checkSession, avisarCierreVoluntario } = useApp();
5479
5704
  const dispatch = (0, import_react_redux4.useDispatch)();
5480
5705
  const {
5481
5706
  control,
@@ -5507,6 +5732,7 @@ var LoginPage = () => {
5507
5732
  const errorText = "Credenciales incorrectas o error al iniciar sesi\xF3n";
5508
5733
  throw new Error(errorText);
5509
5734
  }
5735
+ avisarCierreVoluntario(false);
5510
5736
  setIsLoggedIn(true);
5511
5737
  await dispatch(fetchClientContext()).unwrap();
5512
5738
  console.log("Login exitoso y clientContext cargado.");
@@ -5546,58 +5772,77 @@ var LoginPage = () => {
5546
5772
  }
5547
5773
  ) }),
5548
5774
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.Typography, { variant: "h5", align: "center", gutterBottom: true, sx: { fontWeight: "bold", color: "primary.main" }, children: "Iniciar Sesi\xF3n" }),
5549
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("form", { onSubmit: handleSubmit(onSubmit), className: "space-y-4", children: [
5550
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
5551
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "username", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.Typography, { variant: "subtitle1", component: "div", sx: { fontWeight: "medium" }, children: "Usuario" }) }),
5552
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5553
- import_react_hook_form.Controller,
5554
- {
5555
- name: "username",
5556
- control,
5557
- render: ({ field }) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5558
- import_material24.TextField,
5775
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
5776
+ "form",
5777
+ {
5778
+ onSubmit: handleSubmit(onSubmit),
5779
+ className: "space-y-4",
5780
+ onKeyDown: (evento) => {
5781
+ if (evento.key !== "Enter" || evento.shiftKey || evento.ctrlKey || evento.altKey || evento.metaKey) return;
5782
+ if (!(evento.target instanceof HTMLInputElement)) return;
5783
+ evento.preventDefault();
5784
+ if (!isSubmitting) void handleSubmit(onSubmit)();
5785
+ },
5786
+ children: [
5787
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
5788
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "username", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.Typography, { variant: "subtitle1", component: "div", sx: { fontWeight: "medium" }, children: "Usuario" }) }),
5789
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5790
+ import_react_hook_form.Controller,
5559
5791
  {
5560
- ...field,
5561
- type: "text",
5562
- placeholder: "Ingresa tu usuario",
5563
- fullWidth: true,
5564
- margin: "normal",
5565
- error: !!errors.username,
5566
- helperText: errors.username?.message,
5567
- disabled: isSubmitting
5792
+ name: "username",
5793
+ control,
5794
+ render: ({ field }) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5795
+ import_material24.TextField,
5796
+ {
5797
+ ...field,
5798
+ type: "text",
5799
+ id: "username",
5800
+ autoComplete: "username",
5801
+ placeholder: "Ingresa tu usuario",
5802
+ fullWidth: true,
5803
+ margin: "normal",
5804
+ error: !!errors.username,
5805
+ helperText: errors.username?.message,
5806
+ disabled: isSubmitting
5807
+ }
5808
+ )
5568
5809
  }
5569
5810
  )
5570
- }
5571
- )
5572
- ] }),
5573
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
5574
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "password", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.Typography, { variant: "subtitle1", component: "div", sx: { fontWeight: "medium" }, children: "Contrase\xF1a" }) }),
5575
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5576
- import_react_hook_form.Controller,
5577
- {
5578
- name: "password",
5579
- control,
5580
- render: ({ field }) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5581
- import_material24.TextField,
5811
+ ] }),
5812
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
5813
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "password", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.Typography, { variant: "subtitle1", component: "div", sx: { fontWeight: "medium" }, children: "Contrase\xF1a" }) }),
5814
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5815
+ import_react_hook_form.Controller,
5582
5816
  {
5583
- ...field,
5584
- type: "password",
5585
- placeholder: "Ingresa tu contrase\xF1a",
5586
- fullWidth: true,
5587
- margin: "normal",
5588
- error: !!errors.password,
5589
- helperText: errors.password?.message,
5590
- disabled: isSubmitting
5817
+ name: "password",
5818
+ control,
5819
+ render: ({ field }) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5820
+ import_material24.TextField,
5821
+ {
5822
+ ...field,
5823
+ type: "password",
5824
+ id: "password",
5825
+ autoComplete: "current-password",
5826
+ placeholder: "Ingresa tu contrase\xF1a",
5827
+ fullWidth: true,
5828
+ margin: "normal",
5829
+ error: !!errors.password,
5830
+ helperText: errors.password?.message,
5831
+ disabled: isSubmitting
5832
+ }
5833
+ )
5591
5834
  }
5592
5835
  )
5593
- }
5594
- )
5595
- ] }),
5596
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.Button, { type: "submit", fullWidth: true, variant: "contained", color: "primary", disabled: isSubmitting, sx: { mt: 2 }, children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
5597
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.CircularProgress, { size: 20, sx: { mr: 1 } }),
5598
- "Iniciando sesi\xF3n..."
5599
- ] }) : "Iniciar Sesi\xF3n" })
5600
- ] }),
5836
+ ] }),
5837
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.Button, { type: "submit", fullWidth: true, variant: "contained", color: "primary", disabled: isSubmitting, sx: { mt: 2 }, children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
5838
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.CircularProgress, { size: 20, sx: { mr: 1 } }),
5839
+ "Iniciando sesi\xF3n..."
5840
+ ] }) : "Iniciar Sesi\xF3n" })
5841
+ ]
5842
+ }
5843
+ ),
5844
+ rutaDeRecuperarClave2 && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.Box, { sx: { mt: 2, textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.Link, { href: `${envConfig.backendUrl}${rutaDeRecuperarClave2}`, variant: "body2", underline: "hover", children: "Me olvid\xE9 la clave / es mi primer ingreso" }) }),
5845
+ claveCambiada && !loginError && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.Alert, { severity: "success", sx: { mt: 2 }, children: "La contrase\xF1a se cambi\xF3. Ingres\xE1 con la clave nueva." }),
5601
5846
  loginError && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_material24.Alert, { severity: "error", sx: { mt: 2, maxHeight: "200px" }, children: [
5602
5847
  " ",
5603
5848
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.AlertTitle, { children: "Error" }),
@@ -5613,7 +5858,7 @@ var LoginPage_default = LoginPage;
5613
5858
 
5614
5859
  // src/components/ProcedureForm.tsx
5615
5860
  var import_react33 = __toESM(require("react"), 1);
5616
- var import_react_router_dom6 = require("react-router-dom");
5861
+ var import_react_router_dom8 = require("react-router-dom");
5617
5862
  var import_material26 = require("@mui/material");
5618
5863
 
5619
5864
  // src/pages/procedure-results/SuccessDisplay.tsx
@@ -5640,7 +5885,7 @@ var extendResultsOk = (newComponents) => {
5640
5885
  // src/components/ProcedureForm.tsx
5641
5886
  var import_jsx_runtime32 = require("react/jsx-runtime");
5642
5887
  var ProcedureForm = () => {
5643
- const { procedureName } = (0, import_react_router_dom6.useParams)();
5888
+ const { procedureName } = (0, import_react_router_dom8.useParams)();
5644
5889
  const { clientContext } = useApp();
5645
5890
  const { callApi, loading: apiLoading, error: apiError } = useApiCall();
5646
5891
  const [validationErrors, setValidationErrors] = (0, import_react33.useState)({});
@@ -6058,15 +6303,15 @@ var ProcedureForm = () => {
6058
6303
  var ProcedureForm_default = ProcedureForm;
6059
6304
 
6060
6305
  // src/App.tsx
6061
- var import_react_redux5 = require("react-redux");
6062
- var import_react44 = require("redux-persist/integration/react");
6306
+ var import_react_redux6 = require("react-redux");
6307
+ var import_react45 = require("redux-persist/integration/react");
6063
6308
 
6064
6309
  // src/components/PrivateRoute.tsx
6065
6310
  var import_react34 = require("react");
6066
- var import_react_router_dom7 = require("react-router-dom");
6311
+ var import_react_router_dom9 = require("react-router-dom");
6067
6312
  var import_jsx_runtime33 = require("react/jsx-runtime");
6068
6313
  var PrivateRoute = ({ children }) => {
6069
- const location = (0, import_react_router_dom7.useLocation)();
6314
+ const location = (0, import_react_router_dom9.useLocation)();
6070
6315
  const dispatch = useAppDispatch();
6071
6316
  const clientContextStatus = useClientContextStatus();
6072
6317
  const isAuthenticated = clientContextStatus === "succeeded";
@@ -6074,7 +6319,7 @@ var PrivateRoute = ({ children }) => {
6074
6319
  console.log(`[PrivateRoute] Render: Path=${location.pathname}, AuthStatus=${clientContextStatus}, IsAuthenticated=${isAuthenticated}, IsLoadingAuth=${isLoadingAuth}`);
6075
6320
  (0, import_react34.useEffect)(() => {
6076
6321
  console.log(`[PrivateRoute] Effect: Path=${location.pathname}, AuthStatus=${clientContextStatus}, IsAuthenticated=${isAuthenticated}, IsLoadingAuth=${isLoadingAuth}`);
6077
- if (!isAuthenticated && !isLoadingAuth && location.pathname !== "/login") {
6322
+ if (!isAuthenticated && !isLoadingAuth && location.pathname !== "/login" && !huboCierreVoluntario()) {
6078
6323
  const path = location.pathname + location.search + location.hash;
6079
6324
  console.log(`[PrivateRoute] ACTION: Dispatching setRedirectPath(${path})`);
6080
6325
  dispatch(setRedirectPath(path));
@@ -6092,7 +6337,7 @@ var PrivateRoute = ({ children }) => {
6092
6337
  }
6093
6338
  if (!isAuthenticated) {
6094
6339
  console.log(`[PrivateRoute] User not authenticated, navigating to /login`);
6095
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_router_dom7.Navigate, { to: "/login", replace: true });
6340
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_router_dom9.Navigate, { to: "/login", replace: true });
6096
6341
  }
6097
6342
  console.log(`[PrivateRoute] User authenticated, rendering children.`);
6098
6343
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_jsx_runtime33.Fragment, { children });
@@ -6101,13 +6346,15 @@ var PrivateRoute_default = PrivateRoute;
6101
6346
 
6102
6347
  // src/components/InitialRedirectHandler.tsx
6103
6348
  var import_react35 = require("react");
6104
- var import_react_router_dom8 = require("react-router-dom");
6349
+ var import_react_router_dom10 = require("react-router-dom");
6105
6350
  var InitialRedirectHandler = () => {
6106
- const location = (0, import_react_router_dom8.useLocation)();
6107
- const navigate = (0, import_react_router_dom8.useNavigate)();
6351
+ const location = (0, import_react_router_dom10.useLocation)();
6352
+ const navigate = (0, import_react_router_dom10.useNavigate)();
6108
6353
  const dispatch = useAppDispatch();
6109
6354
  const redirectPath = useRedirectPath();
6110
6355
  const clientContextStatus = useClientContextStatus();
6356
+ const username = useAppSelector((state) => state.clientContext.username);
6357
+ const redirectUsername = useAppSelector((state) => state.router.redirectUsername);
6111
6358
  (0, import_react35.useEffect)(() => {
6112
6359
  dispatch(setCurrentPath(location.pathname + location.search));
6113
6360
  }, [location, dispatch]);
@@ -6117,7 +6364,9 @@ var InitialRedirectHandler = () => {
6117
6364
  }, [location.pathname]);
6118
6365
  (0, import_react35.useEffect)(() => {
6119
6366
  if (clientContextStatus === "succeeded") {
6120
- if (redirectPath && redirectPath !== "/login") {
6367
+ if (redirectPath && redirectPath !== "/login" && redirectUsername && redirectUsername !== username) {
6368
+ dispatch(setRedirectPath(null));
6369
+ } else if (redirectPath && redirectPath !== "/login") {
6121
6370
  console.log("Redirigiendo a la ruta guardada:", redirectPath);
6122
6371
  navegandoRef.current = true;
6123
6372
  navigate(redirectPath);
@@ -6127,14 +6376,14 @@ var InitialRedirectHandler = () => {
6127
6376
  navigate("/");
6128
6377
  }
6129
6378
  }
6130
- }, [clientContextStatus, redirectPath, location.pathname, navigate, dispatch]);
6379
+ }, [clientContextStatus, redirectPath, redirectUsername, username, location.pathname, navigate, dispatch]);
6131
6380
  return null;
6132
6381
  };
6133
6382
  var InitialRedirectHandler_default = InitialRedirectHandler;
6134
6383
 
6135
6384
  // src/pages/GenericDataGridPage.tsx
6136
6385
  var import_react36 = __toESM(require("react"), 1);
6137
- var import_react_router_dom9 = require("react-router-dom");
6386
+ var import_react_router_dom11 = require("react-router-dom");
6138
6387
  var import_material27 = require("@mui/material");
6139
6388
  var import_Close2 = __toESM(require("@mui/icons-material/Close"), 1);
6140
6389
  var import_jsx_runtime34 = require("react/jsx-runtime");
@@ -6143,12 +6392,12 @@ var GenericDataGridPage = ({
6143
6392
  initialFixedFields = [],
6144
6393
  parametros: parametrosRecibidos
6145
6394
  }) => {
6146
- const { tableName: urlTableName } = (0, import_react_router_dom9.useParams)();
6395
+ const { tableName: urlTableName } = (0, import_react_router_dom11.useParams)();
6147
6396
  const effectiveTableName = initialTableName || urlTableName;
6148
6397
  const [tabs, setTabs] = (0, import_react36.useState)([]);
6149
6398
  const [activeTabIdx, setActiveTabIdx] = (0, import_react36.useState)(0);
6150
6399
  const ffKey = JSON.stringify(initialFixedFields);
6151
- const location = (0, import_react_router_dom9.useLocation)();
6400
+ const location = (0, import_react_router_dom11.useLocation)();
6152
6401
  const parametros = (0, import_react36.useMemo)(() => parametrosRecibidos ?? parametrosDeTabla(void 0, location.search), [parametrosRecibidos, location.search]);
6153
6402
  const fixedFieldsIniciales = initialFixedFields.length > 0 ? initialFixedFields : parametros.fixedFields ?? [];
6154
6403
  const parametrosKey = JSON.stringify(parametros);
@@ -6229,11 +6478,11 @@ var GenericDataGridPage_default = GenericDataGridPage;
6229
6478
 
6230
6479
  // src/pages/PantallaDeRuta.tsx
6231
6480
  var import_react37 = require("react");
6232
- var import_react_router_dom10 = require("react-router-dom");
6481
+ var import_react_router_dom12 = require("react-router-dom");
6233
6482
  var import_material28 = require("@mui/material");
6234
6483
  var import_jsx_runtime35 = require("react/jsx-runtime");
6235
6484
  var PantallaDeRuta = ({ screenName }) => {
6236
- const [busqueda] = (0, import_react_router_dom10.useSearchParams)();
6485
+ const [busqueda] = (0, import_react_router_dom12.useSearchParams)();
6237
6486
  const params = (0, import_react37.useMemo)(() => Object.fromEntries(busqueda.entries()), [busqueda]);
6238
6487
  const Pantalla = wScreens[screenName];
6239
6488
  return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_material28.Box, { sx: { pt: 3, pl: 4 }, children: Pantalla ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Pantalla, { screenName, params }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(FallbackWScreen, { screenName }) });
@@ -6241,13 +6490,13 @@ var PantallaDeRuta = ({ screenName }) => {
6241
6490
 
6242
6491
  // src/pages/MenuTablePage.tsx
6243
6492
  var import_react38 = require("react");
6244
- var import_react_router_dom11 = require("react-router-dom");
6493
+ var import_react_router_dom13 = require("react-router-dom");
6245
6494
  var import_material29 = require("@mui/material");
6246
6495
  var import_jsx_runtime36 = require("react/jsx-runtime");
6247
6496
  var MenuTablePage = () => {
6248
- const { "*": menuName } = (0, import_react_router_dom11.useParams)();
6497
+ const { "*": menuName } = (0, import_react_router_dom13.useParams)();
6249
6498
  const { clientContext } = useApp();
6250
- const { search } = (0, import_react_router_dom11.useLocation)();
6499
+ const { search } = (0, import_react_router_dom13.useLocation)();
6251
6500
  const { currentTableName, currentFixedFields, parametros } = (0, import_react38.useMemo)(() => {
6252
6501
  let tableName = void 0;
6253
6502
  let fixedFields = [];
@@ -6287,7 +6536,7 @@ var MenuTablePage_default = MenuTablePage;
6287
6536
 
6288
6537
  // src/puente/PantallaBP.tsx
6289
6538
  var import_react40 = require("react");
6290
- var import_react_router_dom12 = require("react-router-dom");
6539
+ var import_react_router_dom14 = require("react-router-dom");
6291
6540
  var import_material31 = require("@mui/material");
6292
6541
 
6293
6542
  // src/puente/clienteBP.ts
@@ -6490,13 +6739,6 @@ function resolverPantallaBP(myOwn, nombre, contenedor) {
6490
6739
  return null;
6491
6740
  }
6492
6741
 
6493
- // src/puente/habilitacion.ts
6494
- var habilitado = false;
6495
- var habilitarPuenteBP = () => {
6496
- habilitado = true;
6497
- };
6498
- var puenteBPHabilitado = () => habilitado;
6499
-
6500
6742
  // src/puente/PantallaBP.tsx
6501
6743
  var import_jsx_runtime38 = require("react/jsx-runtime");
6502
6744
  function buscarItemDeMenu(items, tipo) {
@@ -6559,18 +6801,18 @@ var PantallaBP = ({ nombre, direccion }) => {
6559
6801
  );
6560
6802
  };
6561
6803
  var PantallaDeRespaldo = () => {
6562
- const { screenName = "" } = (0, import_react_router_dom12.useParams)();
6563
- const { search } = (0, import_react_router_dom12.useLocation)();
6804
+ const { screenName = "" } = (0, import_react_router_dom14.useParams)();
6805
+ const { search } = (0, import_react_router_dom14.useLocation)();
6564
6806
  if (!puenteBPHabilitado()) {
6565
6807
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material31.Box, { sx: { p: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(FallbackWScreen, { screenName }) });
6566
6808
  }
6567
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_router_dom12.Navigate, { to: rutaDePantallaBP(screenName, search), replace: true });
6809
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_router_dom14.Navigate, { to: rutaDePantallaBP(screenName, search), replace: true });
6568
6810
  };
6569
6811
  var PaginaBP = () => {
6570
- const { hash, search } = (0, import_react_router_dom12.useLocation)();
6812
+ const { hash, search } = (0, import_react_router_dom14.useLocation)();
6571
6813
  const direccion = hash || search;
6572
6814
  const nombre = new URLSearchParams(direccion.replace(/^[#?]/, "")).get("w");
6573
- if (!puenteBPHabilitado()) return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_router_dom12.Navigate, { to: "/", replace: true });
6815
+ if (!puenteBPHabilitado()) return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_router_dom14.Navigate, { to: "/", replace: true });
6574
6816
  if (!nombre) return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material31.Box, { sx: { p: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_material31.Alert, { severity: "warning", children: [
6575
6817
  "Falta la pantalla de backend-plus (",
6576
6818
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("code", { children: "#w=..." }),
@@ -6581,7 +6823,7 @@ var PaginaBP = () => {
6581
6823
 
6582
6824
  // src/puente/GrillaBP.tsx
6583
6825
  var import_react41 = require("react");
6584
- var import_react_router_dom13 = require("react-router-dom");
6826
+ var import_react_router_dom15 = require("react-router-dom");
6585
6827
  var import_material32 = require("@mui/material");
6586
6828
  var import_jsx_runtime39 = (
6587
6829
  // el indicador de carga flota: si ocupara lugar, el contenido de backend-plus empezaría más abajo mientras carga y subiría al terminar
@@ -6621,13 +6863,13 @@ var GrillaBP = ({ tabla, fixedFields }) => {
6621
6863
  ] });
6622
6864
  };
6623
6865
  var PaginaGrillaBP = () => {
6624
- const { tableName = "" } = (0, import_react_router_dom13.useParams)();
6866
+ const { tableName = "" } = (0, import_react_router_dom15.useParams)();
6625
6867
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GrillaBP, { tabla: tableName }, tableName);
6626
6868
  };
6627
6869
 
6628
6870
  // src/puente/CapturaBP.tsx
6629
6871
  var import_react42 = require("react");
6630
- var import_react_router_dom14 = require("react-router-dom");
6872
+ var import_react_router_dom16 = require("react-router-dom");
6631
6873
 
6632
6874
  // src/puente/capturaDeNavegacion.ts
6633
6875
  function apiDeNavegacion() {
@@ -6672,62 +6914,175 @@ function capturarNavegacionBP(urlBackend, navegar) {
6672
6914
 
6673
6915
  // src/puente/CapturaBP.tsx
6674
6916
  var CapturaBP = () => {
6675
- const navigate = (0, import_react_router_dom14.useNavigate)();
6917
+ const navigate = (0, import_react_router_dom16.useNavigate)();
6676
6918
  (0, import_react42.useEffect)(() => capturarNavegacionBP(envConfig.backendUrl, navigate), [navigate]);
6677
6919
  return null;
6678
6920
  };
6679
6921
 
6922
+ // src/pages/CambiarClavePage.tsx
6923
+ var import_react43 = require("react");
6924
+ var import_material33 = require("@mui/material");
6925
+ var import_react_router_dom17 = require("react-router-dom");
6926
+ var import_react_redux5 = require("react-redux");
6927
+
6928
+ // src/utils/cambiarClave.ts
6929
+ var MENSAJE_POR_OMISION = "No se pudo cambiar la contrase\xF1a";
6930
+ function resultadoDeCambioDeClave(urlFinal, html) {
6931
+ if (/\/login\/?(\?.*)?$/.test(urlFinal)) return { ok: true };
6932
+ const mensajes = Array.from(new DOMParser().parseFromString(html, "text/html").querySelectorAll(".error-message")).map((elemento) => (elemento.textContent ?? "").trim()).filter((mensaje) => mensaje !== "");
6933
+ return { ok: false, mensaje: mensajes.join("\n") || MENSAJE_POR_OMISION };
6934
+ }
6935
+
6936
+ // src/pages/CambiarClavePage.tsx
6937
+ var import_jsx_runtime40 = require("react/jsx-runtime");
6938
+ var CambiarClavePage = () => {
6939
+ const { clientContext, setIsLoggedIn, avisarCierreVoluntario } = useApp();
6940
+ const dispatch = (0, import_react_redux5.useDispatch)();
6941
+ const navigate = (0, import_react_router_dom17.useNavigate)();
6942
+ const [claveActual, setClaveActual] = (0, import_react43.useState)("");
6943
+ const [claveNueva, setClaveNueva] = (0, import_react43.useState)("");
6944
+ const [claveRepetida, setClaveRepetida] = (0, import_react43.useState)("");
6945
+ const [error, setError] = (0, import_react43.useState)(null);
6946
+ const [enviando, setEnviando] = (0, import_react43.useState)(false);
6947
+ const enviar = async (evento) => {
6948
+ evento.preventDefault();
6949
+ setError(null);
6950
+ if (claveNueva !== claveRepetida) {
6951
+ setError("La clave nueva y su repetici\xF3n no coinciden");
6952
+ return;
6953
+ }
6954
+ setEnviando(true);
6955
+ avisarCierreVoluntario(true);
6956
+ try {
6957
+ const respuesta = await fetchApi("/chpass", {
6958
+ method: "POST",
6959
+ body: new URLSearchParams({ oldPassword: claveActual, newPassword: claveNueva, repPassword: claveRepetida })
6960
+ });
6961
+ const resultado = resultadoDeCambioDeClave(respuesta.url, await respuesta.text());
6962
+ if (!resultado.ok) {
6963
+ avisarCierreVoluntario(false);
6964
+ setError(resultado.mensaje);
6965
+ return;
6966
+ }
6967
+ setIsLoggedIn(false);
6968
+ dispatch(clearClientContext());
6969
+ dispatch(setRedirectPath(null));
6970
+ navigate("/login", { replace: true, state: { claveCambiada: true } });
6971
+ } catch (err) {
6972
+ avisarCierreVoluntario(false);
6973
+ setError(mensajeDeError(err, "No se pudo cambiar la contrase\xF1a"));
6974
+ } finally {
6975
+ setEnviando(false);
6976
+ }
6977
+ };
6978
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.Box, { sx: { p: 3, display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_material33.Box, { component: "form", onSubmit: enviar, sx: { width: "100%", maxWidth: 420 }, children: [
6979
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.Typography, { variant: "h5", gutterBottom: true, children: "Cambiar contrase\xF1a" }),
6980
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.TextField, { label: "Usuario", value: clientContext?.username ?? "", fullWidth: true, margin: "normal", disabled: true, autoComplete: "username" }),
6981
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
6982
+ import_material33.TextField,
6983
+ {
6984
+ label: "Contrase\xF1a actual",
6985
+ type: "password",
6986
+ value: claveActual,
6987
+ onChange: (e) => setClaveActual(e.target.value),
6988
+ fullWidth: true,
6989
+ margin: "normal",
6990
+ autoFocus: true,
6991
+ required: true,
6992
+ autoComplete: "current-password",
6993
+ disabled: enviando
6994
+ }
6995
+ ),
6996
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
6997
+ import_material33.TextField,
6998
+ {
6999
+ label: "Contrase\xF1a nueva",
7000
+ type: "password",
7001
+ value: claveNueva,
7002
+ onChange: (e) => setClaveNueva(e.target.value),
7003
+ fullWidth: true,
7004
+ margin: "normal",
7005
+ required: true,
7006
+ autoComplete: "new-password",
7007
+ disabled: enviando
7008
+ }
7009
+ ),
7010
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
7011
+ import_material33.TextField,
7012
+ {
7013
+ label: "Repetir contrase\xF1a nueva",
7014
+ type: "password",
7015
+ value: claveRepetida,
7016
+ onChange: (e) => setClaveRepetida(e.target.value),
7017
+ fullWidth: true,
7018
+ margin: "normal",
7019
+ required: true,
7020
+ autoComplete: "new-password",
7021
+ disabled: enviando
7022
+ }
7023
+ ),
7024
+ error && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.Alert, { severity: "error", sx: { mt: 1, whiteSpace: "pre-line" }, children: error }),
7025
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_material33.Box, { sx: { display: "flex", gap: 1, justifyContent: "flex-end", mt: 2 }, children: [
7026
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.Button, { onClick: () => navigate(-1), disabled: enviando, children: "Cancelar" }),
7027
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.Button, { type: "submit", variant: "contained", disabled: enviando, children: enviando ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.CircularProgress, { size: 20 }) : "Cambiar contrase\xF1a" })
7028
+ ] }),
7029
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.Typography, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 2 }, children: "Al cambiarla se cierra la sesi\xF3n: hay que ingresar de nuevo con la clave nueva." })
7030
+ ] }) });
7031
+ };
7032
+ var CambiarClavePage_default = CambiarClavePage;
7033
+
6680
7034
  // src/App.tsx
6681
7035
  var import_type_store6 = __toESM(require("type-store"), 1);
6682
- var import_jsx_runtime40 = require("react/jsx-runtime");
7036
+ var import_jsx_runtime41 = require("react/jsx-runtime");
6683
7037
  var LocationTracker = () => {
6684
- const location = (0, import_react_router_dom15.useLocation)();
7038
+ const location = (0, import_react_router_dom18.useLocation)();
6685
7039
  const dispatch = useAppDispatch();
6686
- (0, import_react43.useEffect)(() => {
7040
+ (0, import_react44.useEffect)(() => {
6687
7041
  dispatch(setCurrentPath(location.pathname + location.search));
6688
7042
  }, [location, dispatch]);
6689
7043
  return null;
6690
7044
  };
6691
7045
  function FrontendPlusReactRoutes({ myRoutes, myUnloggedRoutes, layout: Layout = MainLayout_default, unloggedLayout: UnloggedLayout }) {
6692
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
6693
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(InitialRedirectHandler_default, {}),
6694
- puenteBPHabilitado() && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(CapturaBP, {}),
6695
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_router_dom15.Routes, { children: [
6696
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_router_dom15.Route, { element: UnloggedLayout ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(UnloggedLayout, {}) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Outlet, {}), children: [
6697
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Route, { path: "/login", element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LoginPage_default, {}) }),
6698
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Route, { path: "/logout", element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LogoutPage_default, {}) }),
7046
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
7047
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(InitialRedirectHandler_default, {}),
7048
+ puenteBPHabilitado() && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CapturaBP, {}),
7049
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react_router_dom18.Routes, { children: [
7050
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react_router_dom18.Route, { element: UnloggedLayout ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(UnloggedLayout, {}) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Outlet, {}), children: [
7051
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/login", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(LoginPage_default, {}) }),
7052
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/logout", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(LogoutPage_default, {}) }),
6699
7053
  myUnloggedRoutes
6700
7054
  ] }),
6701
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_router_dom15.Route, { element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(PrivateRoute_default, { children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Layout, {}) }), children: [
6702
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Route, { path: "/", element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(PantallaInicial_default, {}) }),
6703
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Route, { path: "/table/:tableName", element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(GenericDataGridPage_default, {}) }),
6704
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Route, { path: "/menu/*", element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MenuTablePage_default, {}) }),
6705
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Route, { path: "/procedures/:procedureName", element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ProcedureForm_default, {}) }),
6706
- Object.keys(wScreens).map((screenName) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
6707
- import_react_router_dom15.Route,
7055
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react_router_dom18.Route, { element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PrivateRoute_default, { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Layout, {}) }), children: [
7056
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PantallaInicial_default, {}) }),
7057
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/table/:tableName", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(GenericDataGridPage_default, {}) }),
7058
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/menu/*", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(MenuTablePage_default, {}) }),
7059
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/procedures/:procedureName", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ProcedureForm_default, {}) }),
7060
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/chpass", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CambiarClavePage_default, {}) }),
7061
+ Object.keys(wScreens).map((screenName) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
7062
+ import_react_router_dom18.Route,
6708
7063
  {
6709
7064
  path: `/wScreens/${screenName}`,
6710
- element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(PantallaDeRuta, { screenName })
7065
+ element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PantallaDeRuta, { screenName })
6711
7066
  },
6712
7067
  screenName
6713
7068
  )),
6714
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Route, { path: "/wScreens-fallback/:screenName", element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(PantallaDeRespaldo, {}) }),
6715
- puenteBPHabilitado() && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Route, { path: "/bp/menu", element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(PaginaBP, {}) }),
6716
- puenteBPHabilitado() && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Route, { path: "/bp/table/:tableName", element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(PaginaGrillaBP, {}) }),
7069
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/wScreens-fallback/:screenName", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PantallaDeRespaldo, {}) }),
7070
+ puenteBPHabilitado() && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/bp/menu", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PaginaBP, {}) }),
7071
+ puenteBPHabilitado() && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/bp/table/:tableName", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PaginaGrillaBP, {}) }),
6717
7072
  myRoutes,
6718
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { style: { marginTop: "20px", marginLeft: "10px" }, children: "404 - Recurso No Encontrado" }) })
7073
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { style: { marginTop: "20px", marginLeft: "10px" }, children: "404 - Recurso No Encontrado" }) })
6719
7074
  ] }),
6720
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_router_dom15.Navigate, { to: "/", replace: true }) })
7075
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Navigate, { to: "/", replace: true }) })
6721
7076
  ] })
6722
7077
  ] });
6723
7078
  }
6724
7079
  var FrontendPlusProviders = ({ children, publicPaths }) => {
6725
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_redux5.Provider, { store, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react44.PersistGate, { loading: null, persistor, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_router_dom15.BrowserRouter, { basename: rutaBase, children: [
6726
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LocationTracker, {}),
7080
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_redux6.Provider, { store, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react45.PersistGate, { loading: null, persistor, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react_router_dom18.BrowserRouter, { basename: rutaBase, children: [
7081
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(LocationTracker, {}),
6727
7082
  " ",
6728
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(AppProvider, { publicPaths, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(SnackbarProvider, { children: [
7083
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(AppProvider, { publicPaths, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(SnackbarProvider, { children: [
6729
7084
  children,
6730
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SessionExpiredMessage_default, {})
7085
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(SessionExpiredMessage_default, {})
6731
7086
  ] }) })
6732
7087
  ] }) }) });
6733
7088
  };
@@ -6739,9 +7094,10 @@ var App = ({
6739
7094
  myClientSides,
6740
7095
  myResultsOk,
6741
7096
  myConditions,
6742
- puenteBP
7097
+ puenteBP,
7098
+ recuperarClave
6743
7099
  }) => {
6744
- (0, import_react43.useMemo)(() => {
7100
+ (0, import_react44.useMemo)(() => {
6745
7101
  if (myClientSides) {
6746
7102
  extendClientSides(myClientSides);
6747
7103
  }
@@ -6757,8 +7113,9 @@ var App = ({
6757
7113
  if (puenteBP) {
6758
7114
  habilitarPuenteBP();
6759
7115
  }
6760
- }, [myClientSides, myResultsOk, myWScreens, myConditions, puenteBP]);
6761
- (0, import_react43.useEffect)(() => {
7116
+ configurarRecuperarClave(recuperarClave);
7117
+ }, [myClientSides, myResultsOk, myWScreens, myConditions, puenteBP, recuperarClave]);
7118
+ (0, import_react44.useEffect)(() => {
6762
7119
  import_type_store6.default.messages = import_type_store6.default.i18n.messages.es;
6763
7120
  import_type_store6.default.locale = import_type_store6.default.i18n.locale.es;
6764
7121
  const link = document.querySelector("link[rel*='icon']") || document.createElement("link");
@@ -6767,11 +7124,11 @@ var App = ({
6767
7124
  link.href = `${envConfig.backendUrl}/img/logo-128.png`;
6768
7125
  document.getElementsByTagName("head")[0].appendChild(link);
6769
7126
  }, []);
6770
- const safePaths = import_react43.default.useMemo(() => {
7127
+ const safePaths = import_react44.default.useMemo(() => {
6771
7128
  const extracted = extractPathsFromRoutes(myUnloggedRoutes);
6772
7129
  return ["/login", "/logout", ...extracted];
6773
7130
  }, [myUnloggedRoutes]);
6774
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(FrontendPlusProviders, { publicPaths: safePaths, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
7131
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(FrontendPlusProviders, { publicPaths: safePaths, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
6775
7132
  FrontendPlusReactRoutes,
6776
7133
  {
6777
7134
  myRoutes,