frontend-plus-react 0.7.0 → 0.8.0

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;
@@ -2048,9 +2064,10 @@ var defaultColumnHeaderCellRenderer = (props, fieldDef, ocultarColumna) => {
2048
2064
  {
2049
2065
  variant: "body2",
2050
2066
  sx: {
2051
- fontWeight: "bold",
2067
+ fontWeight: 500,
2068
+ color: "#37474f",
2052
2069
  textDecoration: isPrimaryKey ? "underline" : "none",
2053
- fontSize: "0.930rem",
2070
+ fontSize: "0.85rem",
2054
2071
  textAlign: "left",
2055
2072
  flexGrow: 1,
2056
2073
  overflow: "hidden",
@@ -2076,22 +2093,20 @@ var ActionsColumnHeaderCell = ({
2076
2093
  allowInsert && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2077
2094
  import_material4.IconButton,
2078
2095
  {
2079
- color: "inherit",
2080
2096
  onClick: () => handleAddRow(),
2081
2097
  size: "small",
2082
2098
  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" } })
2099
+ sx: { p: 0.25, color: "grey.800", "&:hover": { color: "success.main" } },
2100
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_Add.default, { sx: { fontSize: 18 } })
2085
2101
  }
2086
2102
  ),
2087
2103
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2088
2104
  import_material4.IconButton,
2089
2105
  {
2090
- color: "inherit",
2091
2106
  onClick: toggleFilterVisibility,
2092
2107
  size: "small",
2093
2108
  title: isFilterRowVisible ? "Ocultar filtros" : "Mostrar filtros",
2094
- sx: { p: 0.25 },
2109
+ sx: { p: 0.25, color: "grey.800" },
2095
2110
  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
2111
  }
2097
2112
  ),
@@ -2101,7 +2116,7 @@ var ActionsColumnHeaderCell = ({
2101
2116
  onClick: (event) => handleDataGridOptions(event),
2102
2117
  size: "small",
2103
2118
  title: "Opciones de esta tabla",
2104
- sx: { p: 0.25 },
2119
+ sx: { p: 0.25, color: "grey.800" },
2105
2120
  children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_MoreVert.default, { sx: { fontSize: 20, variant: "filled", size: "small" } })
2106
2121
  }
2107
2122
  )
@@ -2582,9 +2597,12 @@ var TypedField = ({ fieldDef, value, onChange, onCommit, disabled, isFilterMode
2582
2597
  "& .MuiOutlinedInput-root": {
2583
2598
  backgroundColor: "background.paper",
2584
2599
  height: FILTER_ROW_HEIGHT - 5,
2585
- minWidth: 50,
2600
+ minWidth: 30,
2586
2601
  fontSize: "0.8rem",
2587
2602
  padding: "0px"
2603
+ },
2604
+ "& .MuiOutlinedInput-input": {
2605
+ padding: "0 4px"
2588
2606
  }
2589
2607
  } : {
2590
2608
  "& .MuiOutlinedInput-root": {
@@ -2608,7 +2626,10 @@ var TypedField = ({ fieldDef, value, onChange, onCommit, disabled, isFilterMode
2608
2626
  };
2609
2627
 
2610
2628
  // src/components/grid/FilterInputRender.tsx
2611
- var import_jsx_runtime12 = require("react/jsx-runtime");
2629
+ var import_jsx_runtime12 = (
2630
+ // como en backend-plus, el operador es un símbolo chico y el campo se queda con el resto del ancho
2631
+ require("react/jsx-runtime")
2632
+ );
2612
2633
  function FilterInputRenderer({
2613
2634
  columnKey,
2614
2635
  currentFilter,
@@ -2631,7 +2652,7 @@ function FilterInputRenderer({
2631
2652
  [columnKey]: { operator: newOp, value: formattedVal }
2632
2653
  }));
2633
2654
  };
2634
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Box, { sx: { mt: 0, display: "flex", alignItems: "center", gap: 0.5, pr: 1 }, children: [
2655
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Box, { sx: { mt: 0, display: "flex", alignItems: "center", gap: "2px", width: "100%" }, children: [
2635
2656
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2636
2657
  "select",
2637
2658
  {
@@ -2639,13 +2660,16 @@ function FilterInputRenderer({
2639
2660
  onChange: (e) => handleFilterChange(e.target.value, val),
2640
2661
  style: {
2641
2662
  height: FILTER_ROW_HEIGHT - 5,
2642
- minWidth: 40,
2643
- backgroundColor: "white",
2644
- border: "1px solid #ccc",
2663
+ width: 22,
2664
+ flexShrink: 0,
2665
+ appearance: "none",
2666
+ backgroundColor: "#CEF",
2667
+ border: "none",
2645
2668
  borderRadius: "4px",
2646
2669
  fontSize: "0.8rem",
2647
- padding: "0 2px",
2670
+ padding: 0,
2648
2671
  textAlign: "center",
2672
+ textAlignLast: "center",
2649
2673
  cursor: "pointer"
2650
2674
  },
2651
2675
  children: filterOperators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: op.value, children: op.label }, op.value))
@@ -2696,7 +2720,7 @@ var defaultColumnSummaryCellRenderer = (props, fixedFields) => {
2696
2720
  const { filters, setFilters } = row;
2697
2721
  if (valorFijoDe(fixedFields, column.key)) return null;
2698
2722
  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)(
2723
+ 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
2724
  FilterInputRender_default,
2701
2725
  {
2702
2726
  columnKey: column.key,
@@ -2923,14 +2947,12 @@ var allColumnsCellRenderer = (props) => {
2923
2947
  }
2924
2948
  ];
2925
2949
  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,
2950
+ import_material13.IconButton,
2927
2951
  {
2928
- variant: "outlined",
2929
- color: actionDef.color,
2930
2952
  size: "small",
2931
2953
  onClick: () => actionDef.handler(row),
2932
2954
  title: actionDef.title,
2933
- sx: { width: 22, minWidth: 22, height: 22, "& .MuiButton-startIcon": { m: 0 } },
2955
+ sx: { width: 22, height: 22, p: 0, color: "grey.800", "&:hover": { color: `${actionDef.color}.main` } },
2934
2956
  children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(actionDef.icon, { sx: { fontSize: 18 } })
2935
2957
  },
2936
2958
  actionDef.action
@@ -3340,20 +3362,36 @@ function campoDibujable(field) {
3340
3362
 
3341
3363
  // src/hooks/grid/useGridColumns.ts
3342
3364
  var estimateTextWidth = (text) => text.length * 8 + 16;
3343
- var calcularAnchosDeColumnas = (tableDefinition, filas) => {
3365
+ var contextoDeMedida;
3366
+ var anchoDeTitulo = (texto) => {
3367
+ if (contextoDeMedida === void 0) {
3368
+ try {
3369
+ contextoDeMedida = document.createElement("canvas").getContext("2d");
3370
+ } catch {
3371
+ contextoDeMedida = null;
3372
+ }
3373
+ }
3374
+ if (!contextoDeMedida) return texto.length * 9;
3375
+ contextoDeMedida.font = "bold 14.9px Roboto, Helvetica, Arial, sans-serif";
3376
+ return Math.ceil(contextoDeMedida.measureText(texto).width);
3377
+ };
3378
+ var EXTRA_TITULO = 8 + 20 + 4;
3379
+ var calcularAnchosDeColumnas = (tableDefinition, filas, anchoDisponible = Infinity) => {
3344
3380
  const anchos = /* @__PURE__ */ new Map();
3381
+ const anchosConTituloCompleto = /* @__PURE__ */ new Map();
3345
3382
  const muestra = filas.slice(0, 50);
3346
3383
  tableDefinition.fields.forEach((fieldDef) => {
3347
3384
  if (typeof fieldDef.width === "number" && fieldDef.width > 0) {
3348
3385
  anchos.set(fieldDef.name, fieldDef.width);
3386
+ anchosConTituloCompleto.set(fieldDef.name, fieldDef.width);
3349
3387
  return;
3350
3388
  }
3351
3389
  const headerLabel = fieldDef.label || fieldDef.name;
3352
3390
  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;
3391
+ let typeMinWidth = 80;
3392
+ if (type.includes("boolean") || type.includes("checkbox")) typeMinWidth = 80;
3393
+ else if (type.includes("date") || type.includes("timestamp")) typeMinWidth = 120;
3394
+ else if (type.includes("integer") || type.includes("numeric")) typeMinWidth = 90;
3357
3395
  const headerWidth = estimateTextWidth(headerLabel) + 20;
3358
3396
  const typer = typerFrom(fieldDef);
3359
3397
  const maxDataWidth = muestra.reduce((maxW, row) => {
@@ -3367,9 +3405,12 @@ var calcularAnchosDeColumnas = (tableDefinition, filas) => {
3367
3405
  }
3368
3406
  return Math.max(maxW, estimateTextWidth(texto));
3369
3407
  }, 0);
3370
- anchos.set(fieldDef.name, Math.min(Math.max(headerWidth, maxDataWidth, typeMinWidth), 500));
3408
+ const compacto = Math.min(Math.max(headerWidth, maxDataWidth, typeMinWidth), 500);
3409
+ anchos.set(fieldDef.name, compacto);
3410
+ anchosConTituloCompleto.set(fieldDef.name, Math.max(compacto, Math.min(anchoDeTitulo(cambiarGuionesBajosPorEspacios(headerLabel)) + EXTRA_TITULO, 500)));
3371
3411
  });
3372
- return anchos;
3412
+ const sumaCompleta = [...anchos.keys()].reduce((total, nombre) => total + (anchosConTituloCompleto.get(nombre) ?? 0), 0);
3413
+ return sumaCompleta <= anchoDisponible ? anchosConTituloCompleto : anchos;
3373
3414
  };
3374
3415
  var useGridColumns = ({
3375
3416
  tableDefinition,
@@ -4094,6 +4135,7 @@ var ALTO_TITULO_ANIDADA = 26;
4094
4135
  var ALTO_MINIMO_CUERPO_ANIDADA = 120;
4095
4136
  var HOLGURA_ANIDADA = 24;
4096
4137
  var ANCHO_MINIMO_ANIDADA = 240;
4138
+ var ANCHO_RESERVADO_PANTALLA = 320;
4097
4139
  var SANGRIA_DETALLE = 28 + 8 + 3;
4098
4140
  var MARGEN_INFERIOR_PAGINA = 24;
4099
4141
  var PRIMERAS_FILAS_ANIDADA = 20;
@@ -4309,7 +4351,7 @@ var GenericDataGrid = ({
4309
4351
  const firstDisplayCount = tableDefinition?.firstDisplayCount ?? PRIMERAS_FILAS_ANIDADA;
4310
4352
  const firstDisplayOverLimit = tableDefinition?.firstDisplayOverLimit ?? LIMITE_PRIMERAS_FILAS_ANIDADA;
4311
4353
  const filasVisiblesMax = anidada ? filteredRows.length <= firstDisplayOverLimit ? filteredRows.length : firstDisplayCount : void 0;
4312
- const conPie = !anidada || !!tableDefinition?.fields.some((field) => field.aggregate);
4354
+ const conPie = !!tableDefinition?.fields.some((field) => field.aggregate);
4313
4355
  const { gridHeight, bottomSummaryRow, summaryRows, filterHeight } = useGridLayout({
4314
4356
  filteredRows,
4315
4357
  isFilterRowVisible,
@@ -4403,7 +4445,7 @@ var GenericDataGrid = ({
4403
4445
  const definition = parametros.tableDef ? fusionar(definicionDelServidor, parametros.tableDef) : definicionDelServidor;
4404
4446
  setTableDefinition(definition);
4405
4447
  setSortColumns(getInitialSortColumns(definition));
4406
- setAnchosCalculados(calcularAnchosDeColumnas(definition, data));
4448
+ setAnchosCalculados(calcularAnchosDeColumnas(definition, data, window.innerWidth - ANCHO_RESERVADO_PANTALLA));
4407
4449
  setColumnasOcultas(columnasOcultasIniciales(definition));
4408
4450
  setTableData(data);
4409
4451
  const filasDeFiltros = parametros.filterColumns ?? aFiltrosIniciales(definition.filterColumns);
@@ -4604,7 +4646,7 @@ var GenericDataGrid = ({
4604
4646
  [exitingRowIds, primaryKey]
4605
4647
  );
4606
4648
  const anchoContenido = (0, import_react26.useMemo)(() => {
4607
- if (!anidada || columns.length === 0) return void 0;
4649
+ if (columns.length === 0) return void 0;
4608
4650
  const sumaColumnas = columns.reduce((total, col) => {
4609
4651
  const medido = columnWidths.get(col.key)?.width;
4610
4652
  return total + (typeof medido === "number" ? medido : typeof col.width === "number" ? col.width : 100);
@@ -4614,8 +4656,9 @@ var GenericDataGrid = ({
4614
4656
  return Math.max(ANCHO_MINIMO_ANIDADA, propio, anchoDetalleMax > 0 ? anchoDetalleMax + SANGRIA_DETALLE + (medidas?.barra ?? 0) + 2 : 0);
4615
4657
  }, [anidada, columns, columnWidths, medidas?.barra, detallesAbiertos, anchosDetalle]);
4616
4658
  (0, import_react26.useEffect)(() => {
4617
- if (anchoContenido !== void 0) onAnchoDeseado?.(anchoContenido);
4618
- }, [onAnchoDeseado, anchoContenido]);
4659
+ if (anidada && anchoContenido !== void 0) onAnchoDeseado?.(anchoContenido);
4660
+ }, [onAnchoDeseado, anidada, anchoContenido]);
4661
+ const anchoExterior = anchoContenido === void 0 ? void 0 : anidada ? anchoContenido : anchoContenido - 2 + 32 + ancestors.length * 32;
4619
4662
  const estiloGrilla = (0, import_react26.useMemo)(() => ({ height: "100%", width: "100%", boxSizing: "border-box", ...gridStyles }), [gridStyles]);
4620
4663
  const bottomSummaryRows = (0, import_react26.useMemo)(() => conPie ? [bottomSummaryRow] : [], [conPie, bottomSummaryRow]);
4621
4664
  const vistaGrilla = (0, import_react26.useMemo)(() => ({ isFilterRowVisible }), [isFilterRowVisible]);
@@ -4641,7 +4684,7 @@ var GenericDataGrid = ({
4641
4684
  if (!tableDefinition) {
4642
4685
  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
4686
  }
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: [
4687
+ 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
4688
  !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
4689
  StaticAncestorGrid,
4647
4690
  {
@@ -4738,7 +4781,7 @@ var GenericDataGrid = ({
4738
4781
  sx: {
4739
4782
  position: "absolute",
4740
4783
  top: filterHeight + HEADER_ROW_HEIGHT + 1,
4741
- bottom: anidada ? (conPie ? SUMMARY_ROW_HEIGHT : 0) + 4 : 102,
4784
+ bottom: anidada ? (conPie ? SUMMARY_ROW_HEIGHT : 0) + 4 : conPie ? 102 : 102 - SUMMARY_ROW_HEIGHT,
4742
4785
  left: 18,
4743
4786
  right: 0,
4744
4787
  display: "flex",
@@ -4863,8 +4906,8 @@ var GenericDataGrid = ({
4863
4906
  var GenericDataGrid_default = GenericDataGrid;
4864
4907
 
4865
4908
  // src/App.tsx
4866
- var import_react43 = __toESM(require("react"), 1);
4867
- var import_react_router_dom15 = require("react-router-dom");
4909
+ var import_react44 = __toESM(require("react"), 1);
4910
+ var import_react_router_dom18 = require("react-router-dom");
4868
4911
 
4869
4912
  // src/components/MainLayout.tsx
4870
4913
  var import_react30 = require("react");
@@ -4875,6 +4918,15 @@ var import_material21 = require("@mui/material");
4875
4918
  var import_react27 = require("react");
4876
4919
  var import_react_router_dom = require("react-router-dom");
4877
4920
  var import_react_redux2 = require("react-redux");
4921
+
4922
+ // src/utils/cierreVoluntario.ts
4923
+ var cierreVoluntario = false;
4924
+ var marcarCierreVoluntario = (activo) => {
4925
+ cierreVoluntario = activo;
4926
+ };
4927
+ var huboCierreVoluntario = () => cierreVoluntario;
4928
+
4929
+ // src/contexts/AppContext.tsx
4878
4930
  var import_jsx_runtime24 = require("react/jsx-runtime");
4879
4931
  var AuthContext = (0, import_react27.createContext)(void 0);
4880
4932
  var useApp = () => {
@@ -4891,9 +4943,10 @@ var AppProvider = ({ children, publicPaths = [] }) => {
4891
4943
  const [showSessionExpiredMessage, setShowSessionExpiredMessage] = (0, import_react27.useState)(false);
4892
4944
  const isRedirectingRef = (0, import_react27.useRef)(false);
4893
4945
  const huboSesionRef = (0, import_react27.useRef)(false);
4946
+ const cierreVoluntarioRef = (0, import_react27.useRef)(false);
4894
4947
  const dispatch = (0, import_react_redux2.useDispatch)();
4895
4948
  const clientContext = (0, import_react_redux2.useSelector)((state) => state.clientContext);
4896
- if (clientContext.status === "succeeded") huboSesionRef.current = true;
4949
+ if (clientContext.status === "succeeded" && !cierreVoluntarioRef.current) huboSesionRef.current = true;
4897
4950
  const navigate = (0, import_react_router_dom.useNavigate)();
4898
4951
  const handleExpiredSessionRedirect = (0, import_react27.useCallback)(() => {
4899
4952
  isRedirectingRef.current = true;
@@ -4904,6 +4957,17 @@ var AppProvider = ({ children, publicPaths = [] }) => {
4904
4957
  isRedirectingRef.current = false;
4905
4958
  }, 2e3);
4906
4959
  }, [navigate]);
4960
+ const avisarCierreVoluntario = (0, import_react27.useCallback)((activo) => {
4961
+ cierreVoluntarioRef.current = activo;
4962
+ marcarCierreVoluntario(activo);
4963
+ if (activo) huboSesionRef.current = false;
4964
+ }, []);
4965
+ const limpiarContextoPorSesionPerdida = (0, import_react27.useCallback)(() => {
4966
+ if (clientContext.status === "succeeded" && clientContext.username !== "" && !cierreVoluntarioRef.current) {
4967
+ dispatch(setRedirectUsername(clientContext.username));
4968
+ }
4969
+ dispatch(clearClientContext());
4970
+ }, [dispatch, clientContext.status, clientContext.username]);
4907
4971
  const location = (0, import_react_router_dom.useLocation)();
4908
4972
  const isCurrentPathPublic = (0, import_react27.useCallback)(() => {
4909
4973
  const current = location.pathname;
@@ -4929,8 +4993,8 @@ var AppProvider = ({ children, publicPaths = [] }) => {
4929
4993
  }
4930
4994
  } else {
4931
4995
  setIsLoggedIn(false);
4932
- dispatch(clearClientContext());
4933
- if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current) {
4996
+ limpiarContextoPorSesionPerdida();
4997
+ if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current && !cierreVoluntarioRef.current) {
4934
4998
  console.warn("Sesi\xF3n inv\xE1lida en ruta protegida. Mostrando modal.");
4935
4999
  setShowSessionExpiredMessage(true);
4936
5000
  } else {
@@ -4940,13 +5004,13 @@ var AppProvider = ({ children, publicPaths = [] }) => {
4940
5004
  } catch (error) {
4941
5005
  console.error("Error checking session:", error);
4942
5006
  setIsLoggedIn(false);
4943
- dispatch(clearClientContext());
5007
+ limpiarContextoPorSesionPerdida();
4944
5008
  const isPublic = publicPaths.some((p) => location.pathname === p || location.pathname.startsWith(p + "/"));
4945
- if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current) {
5009
+ if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current && !cierreVoluntarioRef.current) {
4946
5010
  setShowSessionExpiredMessage(true);
4947
5011
  }
4948
5012
  }
4949
- }, [dispatch, clientContext.status, showSessionExpiredMessage, isCurrentPathPublic]);
5013
+ }, [dispatch, clientContext.status, showSessionExpiredMessage, isCurrentPathPublic, limpiarContextoPorSesionPerdida]);
4950
5014
  (0, import_react27.useEffect)(() => {
4951
5015
  checkSession();
4952
5016
  const intervalId = setInterval(checkSession, 15 * 1e3);
@@ -4960,20 +5024,24 @@ var AppProvider = ({ children, publicPaths = [] }) => {
4960
5024
  checkSession,
4961
5025
  showSessionExpiredMessage,
4962
5026
  setShowSessionExpiredMessage,
4963
- handleExpiredSessionRedirect
5027
+ handleExpiredSessionRedirect,
5028
+ avisarCierreVoluntario
4964
5029
  }), [
4965
5030
  isLoggedIn,
4966
5031
  setIsLoggedIn,
4967
5032
  checkSession,
4968
5033
  showSessionExpiredMessage,
4969
5034
  setShowSessionExpiredMessage,
4970
- handleExpiredSessionRedirect
5035
+ handleExpiredSessionRedirect,
5036
+ avisarCierreVoluntario
4971
5037
  ]);
4972
5038
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AuthContext.Provider, { value: authContextValue, children });
4973
5039
  };
4974
5040
 
4975
5041
  // src/components/MainLayout.tsx
5042
+ var import_react_router_dom5 = require("react-router-dom");
4976
5043
  var import_Logout = __toESM(require("@mui/icons-material/Logout"), 1);
5044
+ var import_VpnKey = __toESM(require("@mui/icons-material/VpnKey"), 1);
4977
5045
  var import_Menu = __toESM(require("@mui/icons-material/Menu"), 1);
4978
5046
  var import_ChevronLeft = __toESM(require("@mui/icons-material/ChevronLeft"), 1);
4979
5047
 
@@ -4987,11 +5055,18 @@ var import_Folder = __toESM(require("@mui/icons-material/Folder"), 1);
4987
5055
  var import_Dns = __toESM(require("@mui/icons-material/Dns"), 1);
4988
5056
  var import_WarningAmber3 = __toESM(require("@mui/icons-material/WarningAmber"), 1);
4989
5057
  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);
5058
+ var import_Search3 = __toESM(require("@mui/icons-material/Search"), 1);
5059
+ var import_Clear = __toESM(require("@mui/icons-material/Clear"), 1);
4992
5060
  var import_react_router_dom2 = require("react-router-dom");
4993
5061
  var import_colors = require("@mui/material/colors");
4994
5062
 
5063
+ // src/puente/habilitacion.ts
5064
+ var habilitado = false;
5065
+ var habilitarPuenteBP = () => {
5066
+ habilitado = true;
5067
+ };
5068
+ var puenteBPHabilitado = () => habilitado;
5069
+
4995
5070
  // src/utils/menuInicial.ts
4996
5071
  function rutaDeTablaDeMenu(camino) {
4997
5072
  return `/menu/${camino.map(encodeURIComponent).join("/")}`;
@@ -5042,16 +5117,93 @@ function itemInicial(menu) {
5042
5117
  return elegido.menuType === "menu" ? itemInicial(elegido.menuContent) : elegido;
5043
5118
  }
5044
5119
 
5120
+ // src/utils/busquedaDeMenu.ts
5121
+ function etiquetaDeItem(item) {
5122
+ return item.label || item.name;
5123
+ }
5124
+ function claveDeMenu(camino) {
5125
+ return camino.join("/");
5126
+ }
5127
+ function hojasDeMenu(menu, grupos = [], etiquetasDeGrupos = []) {
5128
+ return menu.flatMap(
5129
+ (item) => item.menuType === "menu" ? hojasDeMenu(item.menuContent, [...grupos, item.name], [...etiquetasDeGrupos, etiquetaDeItem(item)]) : [{ item, grupos, etiquetasDeGrupos, clave: claveDeMenu([...grupos, item.name]) }]
5130
+ );
5131
+ }
5132
+ function normalizarParaBuscar(texto) {
5133
+ return texto.normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/_/g, " ").toLowerCase();
5134
+ }
5135
+ function buscarEnMenu(hojas, consulta) {
5136
+ const palabras = normalizarParaBuscar(consulta).split(/\s+/).filter((palabra) => palabra !== "");
5137
+ if (palabras.length === 0) return [];
5138
+ const propias = [];
5139
+ const porGrupo = [];
5140
+ for (const hoja of hojas) {
5141
+ const propia = normalizarParaBuscar(`${etiquetaDeItem(hoja.item)} ${hoja.item.name}`);
5142
+ const completa = `${hoja.etiquetasDeGrupos.map(normalizarParaBuscar).join(" ")} ${propia}`;
5143
+ if (!palabras.every((palabra) => completa.includes(palabra))) continue;
5144
+ (palabras.every((palabra) => propia.includes(palabra)) ? propias : porGrupo).push(hoja);
5145
+ }
5146
+ return [...propias, ...porGrupo];
5147
+ }
5148
+
5045
5149
  // src/components/SideMenu.tsx
5046
5150
  var import_jsx_runtime25 = require("react/jsx-runtime");
5047
- var MenuListItem = ({ item, level, grupos = [], onMenuItemClick }) => {
5151
+ var CLAVE_RECIENTES = "fpr.menu.recientes";
5152
+ var MAXIMO_RECIENTES = 4;
5153
+ var getIcon = (item, destacado = false) => {
5154
+ switch (item.menuType) {
5155
+ case "table":
5156
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_TableChart.default, { sx: destacado ? { color: import_colors.blue[700] } : void 0 });
5157
+ case "proc":
5158
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_Dns.default, { sx: destacado ? { color: import_colors.teal[700] } : void 0 });
5159
+ case "menu":
5160
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_Folder.default, { sx: destacado ? { color: import_colors.orange[800] } : void 0 });
5161
+ default:
5162
+ if (wScreens[item.menuType] !== void 0 || puenteBPHabilitado()) {
5163
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_DesktopWindows.default, { sx: destacado ? { color: import_colors.indigo[500] } : void 0 });
5164
+ }
5165
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_WarningAmber3.default, { sx: destacado ? { color: import_colors.red[500] } : void 0 });
5166
+ }
5167
+ };
5168
+ var estaSeleccionado = (item, grupos, currentPath, hash = "") => {
5169
+ const pathParts = currentPath.split("/");
5170
+ const urlTableName = pathParts[1] === "table" ? pathParts[2] : null;
5171
+ if (item.menuType === "table") {
5172
+ return currentPath === rutaDeTablaDeMenu([...grupos, item.name]) || urlTableName === (item.table || item.name);
5173
+ } else if (item.menuType === "proc") {
5174
+ return currentPath === `/procedures/${item.name}`;
5175
+ } else if (item.menuType !== "menu") {
5176
+ return currentPath.startsWith(`/wScreens/${item.menuType}`) || currentPath.startsWith(`/wScreens-fallback/${item.menuType}`) || currentPath === "/bp/menu" && new URLSearchParams(hash.replace(/^[#?]/, "")).get("w") === item.menuType;
5177
+ }
5178
+ return false;
5179
+ };
5180
+ var leerRecientes = () => {
5181
+ try {
5182
+ const guardado = JSON.parse(localStorage.getItem(CLAVE_RECIENTES) ?? "[]");
5183
+ return Array.isArray(guardado) ? guardado.filter((clave) => typeof clave === "string") : [];
5184
+ } catch {
5185
+ return [];
5186
+ }
5187
+ };
5188
+ var guardarRecientes = (recientes) => {
5189
+ try {
5190
+ localStorage.setItem(CLAVE_RECIENTES, JSON.stringify(recientes));
5191
+ } catch {
5192
+ }
5193
+ };
5194
+ var MenuListItem = ({ item, level, grupos = [], hermanos = [], onMenuItemClick }) => {
5048
5195
  const navigate = (0, import_react_router_dom2.useNavigate)();
5049
5196
  const location = (0, import_react_router_dom2.useLocation)();
5050
- const open = useSubMenuOpenState(item.name);
5197
+ const clave = claveDeMenu([...grupos, item.name]);
5198
+ const open = useSubMenuOpenState(clave);
5051
5199
  const dispatch = useAppDispatch();
5052
5200
  const handleClick = () => {
5053
5201
  if (item.menuType === "menu") {
5054
- dispatch(toggleSubMenu(item.name));
5202
+ if (open) {
5203
+ dispatch(setSubMenuOpen({ menuName: clave, isOpen: false }));
5204
+ } else {
5205
+ dispatch(abrirGrupo({ clave, hermanos }));
5206
+ }
5055
5207
  } else {
5056
5208
  navigate(rutaDeItemDeMenu(item, grupos));
5057
5209
  if (onMenuItemClick) {
@@ -5059,39 +5211,14 @@ var MenuListItem = ({ item, level, grupos = [], onMenuItemClick }) => {
5059
5211
  }
5060
5212
  }
5061
5213
  };
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
- }
5214
+ const isSelected = estaSeleccionado(item, grupos, location.pathname, location.hash);
5089
5215
  const itemRef = import_react28.default.useRef(null);
5090
5216
  (0, import_react28.useEffect)(() => {
5091
5217
  if (isSelected && itemRef.current) {
5092
5218
  itemRef.current.scrollIntoView({ behavior: "smooth", block: "nearest" });
5093
5219
  }
5094
5220
  }, [isSelected]);
5221
+ const contenido = item.menuType === "menu" ? item.menuContent : [];
5095
5222
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { ref: itemRef, children: [
5096
5223
  /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
5097
5224
  import_material20.ListItemButton,
@@ -5100,83 +5227,156 @@ var MenuListItem = ({ item, level, grupos = [], onMenuItemClick }) => {
5100
5227
  sx: { pl: level * 2 },
5101
5228
  selected: isSelected,
5102
5229
  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 }),
5230
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.ListItemIcon, { sx: { minWidth: "38px" }, children: getIcon(item, isSelected || open) }),
5231
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.ListItemText, { primary: etiquetaDeItem(item) }),
5105
5232
  item.menuType === "menu" ? open ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ExpandLess.default, {}) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ExpandMore.default, {}) : null
5106
5233
  ]
5107
5234
  }
5108
5235
  ),
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)(
5236
+ 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
5237
  MenuListItem,
5111
5238
  {
5112
5239
  item: subItem,
5113
5240
  level: level + 1,
5114
5241
  grupos: [...grupos, item.name],
5242
+ hermanos: contenido.filter((otro) => otro !== subItem).map((otro) => claveDeMenu([...grupos, item.name, otro.name])),
5115
5243
  onMenuItemClick
5116
5244
  },
5117
5245
  subItem.name
5118
5246
  )) }) })
5119
5247
  ] });
5120
5248
  };
5249
+ var ItemPlano = ({ hoja, seleccionado, traerALaVista = false, onElegir }) => {
5250
+ const boton = (0, import_react28.useRef)(null);
5251
+ (0, import_react28.useEffect)(() => {
5252
+ if (traerALaVista && seleccionado) boton.current?.scrollIntoView({ block: "nearest" });
5253
+ }, [traerALaVista, seleccionado]);
5254
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_material20.ListItemButton, { dense: true, selected: seleccionado, onClick: () => onElegir(hoja), ref: boton, children: [
5255
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.ListItemIcon, { sx: { minWidth: "38px" }, children: getIcon(hoja.item, seleccionado) }),
5256
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5257
+ import_material20.ListItemText,
5258
+ {
5259
+ primary: etiquetaDeItem(hoja.item),
5260
+ secondary: hoja.etiquetasDeGrupos.join(" \u203A ") || void 0,
5261
+ secondaryTypographyProps: { noWrap: true }
5262
+ }
5263
+ )
5264
+ ] });
5265
+ };
5121
5266
  var SideMenu = ({ onMenuItemClick }) => {
5122
5267
  const { clientContext } = useApp();
5123
5268
  const navigate = (0, import_react_router_dom2.useNavigate)();
5124
5269
  const location = (0, import_react_router_dom2.useLocation)();
5125
5270
  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) {
5271
+ const [consulta, setConsulta] = (0, import_react28.useState)("");
5272
+ const [indice, setIndice] = (0, import_react28.useState)(0);
5273
+ const [recientes, setRecientes] = (0, import_react28.useState)(leerRecientes);
5274
+ const campoDeBusqueda = (0, import_react28.useRef)(null);
5275
+ const menu = clientContext?.menu;
5276
+ const hojas = (0, import_react28.useMemo)(() => hojasDeMenu(menu ?? []), [menu]);
5277
+ const resultados = (0, import_react28.useMemo)(() => buscarEnMenu(hojas, consulta), [hojas, consulta]);
5278
+ (0, import_react28.useEffect)(() => {
5279
+ const alApretar = (evento) => {
5280
+ if ((evento.ctrlKey || evento.metaKey) && !evento.altKey && !evento.shiftKey && evento.key.toLowerCase() === "k") {
5281
+ evento.preventDefault();
5282
+ dispatch(setDrawerOpen(true));
5283
+ setTimeout(() => {
5284
+ campoDeBusqueda.current?.focus();
5285
+ campoDeBusqueda.current?.select();
5286
+ }, 50);
5287
+ }
5288
+ };
5289
+ window.addEventListener("keydown", alApretar);
5290
+ return () => window.removeEventListener("keydown", alApretar);
5291
+ }, [dispatch]);
5292
+ (0, import_react28.useEffect)(() => {
5293
+ const actual = hojas.find((hoja) => estaSeleccionado(hoja.item, hoja.grupos, location.pathname, location.hash));
5294
+ if (!actual) return;
5295
+ dispatch(abrirCamino(actual.grupos.map((_, i) => claveDeMenu(actual.grupos.slice(0, i + 1)))));
5296
+ setRecientes((anteriores) => {
5297
+ if (anteriores.includes(actual.clave)) return anteriores;
5298
+ const nuevos = [actual.clave, ...anteriores].slice(0, MAXIMO_RECIENTES);
5299
+ guardarRecientes(nuevos);
5300
+ return nuevos;
5301
+ });
5302
+ }, [location.pathname, location.hash, hojas, dispatch]);
5303
+ if (!menu) {
5134
5304
  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
5305
  }
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;
5306
+ const elegir = (hoja) => {
5307
+ navigate(rutaDeItemDeMenu(hoja.item, hoja.grupos));
5308
+ setConsulta("");
5309
+ setIndice(0);
5310
+ if (onMenuItemClick) {
5311
+ onMenuItemClick();
5312
+ }
5313
+ };
5314
+ const alApretarEnBusqueda = (evento) => {
5315
+ if (evento.key === "ArrowDown" || evento.key === "ArrowUp") {
5316
+ evento.preventDefault();
5317
+ if (resultados.length === 0) return;
5318
+ setIndice((actual) => (actual + (evento.key === "ArrowDown" ? 1 : -1) + resultados.length) % resultados.length);
5319
+ } else if (evento.key === "Enter") {
5320
+ evento.preventDefault();
5321
+ const hoja = resultados[indice];
5322
+ if (hoja) elegir(hoja);
5323
+ } else if (evento.key === "Escape" && consulta !== "") {
5324
+ evento.stopPropagation();
5325
+ setConsulta("");
5326
+ }
5327
+ };
5328
+ const hojasRecientes = recientes.map((clave) => hojas.find((hoja) => hoja.clave === clave)).filter((hoja) => hoja !== void 0);
5329
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_material20.Box, { sx: { width: "100%", flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }, children: [
5330
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Box, { sx: { px: 1.5, py: 1, flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5331
+ import_material20.TextField,
5332
+ {
5333
+ inputRef: campoDeBusqueda,
5334
+ value: consulta,
5335
+ onChange: (evento) => {
5336
+ setConsulta(evento.target.value);
5337
+ setIndice(0);
5338
+ },
5339
+ onKeyDown: alApretarEnBusqueda,
5340
+ placeholder: "Buscar (Ctrl+K)",
5341
+ size: "small",
5342
+ fullWidth: true,
5343
+ autoComplete: "off",
5344
+ sx: { "& .MuiInputBase-input": { fontSize: "0.8rem" } },
5345
+ InputProps: {
5346
+ startAdornment: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.InputAdornment, { position: "start", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_Search3.default, { fontSize: "small" }) }),
5347
+ 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: () => {
5348
+ setConsulta("");
5349
+ campoDeBusqueda.current?.focus();
5350
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_Clear.default, { fontSize: "small" }) }) }) : void 0
5159
5351
  }
5160
5352
  }
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
- ] }) }),
5353
+ ) }),
5170
5354
  /* @__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
- )) })
5355
+ /* @__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: [
5356
+ hojasRecientes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
5357
+ /* @__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" }),
5358
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.List, { disablePadding: true, children: hojasRecientes.map((hoja) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5359
+ ItemPlano,
5360
+ {
5361
+ hoja,
5362
+ seleccionado: estaSeleccionado(hoja.item, hoja.grupos, location.pathname, location.hash),
5363
+ onElegir: elegir
5364
+ },
5365
+ hoja.clave
5366
+ )) }),
5367
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.Divider, {})
5368
+ ] }),
5369
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_material20.List, { children: menu.map((menuItem) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5370
+ MenuListItem,
5371
+ {
5372
+ item: menuItem,
5373
+ level: 1,
5374
+ hermanos: menu.filter((otro) => otro !== menuItem).map((otro) => claveDeMenu([otro.name])),
5375
+ onMenuItemClick
5376
+ },
5377
+ menuItem.name
5378
+ )) })
5379
+ ] }) })
5180
5380
  ] });
5181
5381
  };
5182
5382
  var SideMenu_default = SideMenu;
@@ -5186,10 +5386,11 @@ var import_react29 = require("react");
5186
5386
  var import_react_router_dom3 = require("react-router-dom");
5187
5387
  var import_react_redux3 = require("react-redux");
5188
5388
  var useLogout = () => {
5189
- const { setIsLoggedIn } = useApp();
5389
+ const { setIsLoggedIn, avisarCierreVoluntario } = useApp();
5190
5390
  const dispatch = (0, import_react_redux3.useDispatch)();
5191
5391
  const navigate = (0, import_react_router_dom3.useNavigate)();
5192
5392
  const logout = (0, import_react29.useCallback)(async () => {
5393
+ avisarCierreVoluntario(true);
5193
5394
  try {
5194
5395
  const response = await fetchApi("/logout", { method: "GET" });
5195
5396
  if (response.status === 401 || response.ok) {
@@ -5202,9 +5403,10 @@ var useLogout = () => {
5202
5403
  } finally {
5203
5404
  setIsLoggedIn(false);
5204
5405
  dispatch(clearClientContext());
5406
+ dispatch(setRedirectPath(null));
5205
5407
  navigate("/login", { replace: true });
5206
5408
  }
5207
- }, [setIsLoggedIn, dispatch, navigate]);
5409
+ }, [setIsLoggedIn, avisarCierreVoluntario, dispatch, navigate]);
5208
5410
  return logout;
5209
5411
  };
5210
5412
  var useLogout_default = useLogout;
@@ -5215,6 +5417,7 @@ var drawerWidth = 240;
5215
5417
  var MainLayout = () => {
5216
5418
  const { clientContext } = useApp();
5217
5419
  const logout = useLogout_default();
5420
+ const navigate = (0, import_react_router_dom5.useNavigate)();
5218
5421
  const isDrawerOpen = useIsDrawerOpen();
5219
5422
  const dispatch = useAppDispatch();
5220
5423
  const theme = (0, import_material21.useTheme)();
@@ -5280,6 +5483,7 @@ var MainLayout = () => {
5280
5483
  ),
5281
5484
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_material21.Typography, { variant: "h6", noWrap: true, component: "div", sx: { flexGrow: 1 }, children: isDrawerOpen ? "" : clientContext.config.title }),
5282
5485
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_material21.Typography, { variant: "subtitle1", component: "div", sx: { mr: 2 }, children: clientContext.username }),
5486
+ /* @__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
5487
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5284
5488
  import_material21.Button,
5285
5489
  {
@@ -5304,8 +5508,9 @@ var MainLayout = () => {
5304
5508
  "& .MuiDrawer-paper": {
5305
5509
  width: drawerWidth,
5306
5510
  boxSizing: "border-box",
5307
- overflowX: "hidden",
5308
- overflowY: "auto"
5511
+ overflow: "hidden",
5512
+ display: "flex",
5513
+ flexDirection: "column"
5309
5514
  }
5310
5515
  },
5311
5516
  children: [
@@ -5317,6 +5522,8 @@ var MainLayout = () => {
5317
5522
  alignItems: "center",
5318
5523
  justifyContent: "flex-start",
5319
5524
  px: 1,
5525
+ flexShrink: 0,
5526
+ // el logo queda fijo: solo scrollea la lista del menú
5320
5527
  fontSize: "1.3rem",
5321
5528
  minHeight: theme.mixins.toolbar.minHeight,
5322
5529
  gap: 1.5
@@ -5426,12 +5633,12 @@ var SessionExpiredMessage = () => {
5426
5633
  var SessionExpiredMessage_default = SessionExpiredMessage;
5427
5634
 
5428
5635
  // src/pages/PantallaInicial.tsx
5429
- var import_react_router_dom5 = require("react-router-dom");
5636
+ var import_react_router_dom6 = require("react-router-dom");
5430
5637
  var import_jsx_runtime28 = require("react/jsx-runtime");
5431
5638
  var PantallaInicial = () => {
5432
5639
  const { clientContext } = useApp();
5433
5640
  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;
5641
+ 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
5642
  };
5436
5643
  var PantallaInicial_default = PantallaInicial;
5437
5644
 
@@ -5461,6 +5668,17 @@ var LogoutPage_default = LogoutPage;
5461
5668
  // src/pages/LoginPage.tsx
5462
5669
  var import_react32 = require("react");
5463
5670
  var import_material24 = require("@mui/material");
5671
+ var import_react_router_dom7 = require("react-router-dom");
5672
+
5673
+ // src/pages/configuracionLogin.ts
5674
+ var RUTA_POR_OMISION = "/new-pass";
5675
+ var rutaDeRecuperarClave = null;
5676
+ var configurarRecuperarClave = (recuperarClave) => {
5677
+ rutaDeRecuperarClave = recuperarClave === true ? RUTA_POR_OMISION : typeof recuperarClave === "string" && recuperarClave !== "" ? recuperarClave : null;
5678
+ };
5679
+ var rutaParaRecuperarClave = () => rutaDeRecuperarClave;
5680
+
5681
+ // src/pages/LoginPage.tsx
5464
5682
  var import_react_hook_form = require("react-hook-form");
5465
5683
  var import_zod = require("@hookform/resolvers/zod");
5466
5684
  var z = __toESM(require("zod"), 1);
@@ -5475,7 +5693,10 @@ var loginSchema = z.object({
5475
5693
  var LoginPage = () => {
5476
5694
  const [loginError, setLoginError] = (0, import_react32.useState)(null);
5477
5695
  const [isSubmitting, setIsSubmitting] = (0, import_react32.useState)(false);
5478
- const { setIsLoggedIn, checkSession } = useApp();
5696
+ const estadoDeLaRuta = (0, import_react_router_dom7.useLocation)().state;
5697
+ const claveCambiada = typeof estadoDeLaRuta === "object" && estadoDeLaRuta !== null && "claveCambiada" in estadoDeLaRuta && estadoDeLaRuta.claveCambiada === true;
5698
+ const rutaDeRecuperarClave2 = rutaParaRecuperarClave();
5699
+ const { setIsLoggedIn, checkSession, avisarCierreVoluntario } = useApp();
5479
5700
  const dispatch = (0, import_react_redux4.useDispatch)();
5480
5701
  const {
5481
5702
  control,
@@ -5507,6 +5728,7 @@ var LoginPage = () => {
5507
5728
  const errorText = "Credenciales incorrectas o error al iniciar sesi\xF3n";
5508
5729
  throw new Error(errorText);
5509
5730
  }
5731
+ avisarCierreVoluntario(false);
5510
5732
  setIsLoggedIn(true);
5511
5733
  await dispatch(fetchClientContext()).unwrap();
5512
5734
  console.log("Login exitoso y clientContext cargado.");
@@ -5546,58 +5768,77 @@ var LoginPage = () => {
5546
5768
  }
5547
5769
  ) }),
5548
5770
  /* @__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,
5771
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
5772
+ "form",
5773
+ {
5774
+ onSubmit: handleSubmit(onSubmit),
5775
+ className: "space-y-4",
5776
+ onKeyDown: (evento) => {
5777
+ if (evento.key !== "Enter" || evento.shiftKey || evento.ctrlKey || evento.altKey || evento.metaKey) return;
5778
+ if (!(evento.target instanceof HTMLInputElement)) return;
5779
+ evento.preventDefault();
5780
+ if (!isSubmitting) void handleSubmit(onSubmit)();
5781
+ },
5782
+ children: [
5783
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
5784
+ /* @__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" }) }),
5785
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5786
+ import_react_hook_form.Controller,
5559
5787
  {
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
5788
+ name: "username",
5789
+ control,
5790
+ render: ({ field }) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5791
+ import_material24.TextField,
5792
+ {
5793
+ ...field,
5794
+ type: "text",
5795
+ id: "username",
5796
+ autoComplete: "username",
5797
+ placeholder: "Ingresa tu usuario",
5798
+ fullWidth: true,
5799
+ margin: "normal",
5800
+ error: !!errors.username,
5801
+ helperText: errors.username?.message,
5802
+ disabled: isSubmitting
5803
+ }
5804
+ )
5568
5805
  }
5569
5806
  )
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,
5807
+ ] }),
5808
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
5809
+ /* @__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" }) }),
5810
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5811
+ import_react_hook_form.Controller,
5582
5812
  {
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
5813
+ name: "password",
5814
+ control,
5815
+ render: ({ field }) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
5816
+ import_material24.TextField,
5817
+ {
5818
+ ...field,
5819
+ type: "password",
5820
+ id: "password",
5821
+ autoComplete: "current-password",
5822
+ placeholder: "Ingresa tu contrase\xF1a",
5823
+ fullWidth: true,
5824
+ margin: "normal",
5825
+ error: !!errors.password,
5826
+ helperText: errors.password?.message,
5827
+ disabled: isSubmitting
5828
+ }
5829
+ )
5591
5830
  }
5592
5831
  )
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
- ] }),
5832
+ ] }),
5833
+ /* @__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: [
5834
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.CircularProgress, { size: 20, sx: { mr: 1 } }),
5835
+ "Iniciando sesi\xF3n..."
5836
+ ] }) : "Iniciar Sesi\xF3n" })
5837
+ ]
5838
+ }
5839
+ ),
5840
+ 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" }) }),
5841
+ 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
5842
  loginError && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_material24.Alert, { severity: "error", sx: { mt: 2, maxHeight: "200px" }, children: [
5602
5843
  " ",
5603
5844
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.AlertTitle, { children: "Error" }),
@@ -5613,7 +5854,7 @@ var LoginPage_default = LoginPage;
5613
5854
 
5614
5855
  // src/components/ProcedureForm.tsx
5615
5856
  var import_react33 = __toESM(require("react"), 1);
5616
- var import_react_router_dom6 = require("react-router-dom");
5857
+ var import_react_router_dom8 = require("react-router-dom");
5617
5858
  var import_material26 = require("@mui/material");
5618
5859
 
5619
5860
  // src/pages/procedure-results/SuccessDisplay.tsx
@@ -5640,7 +5881,7 @@ var extendResultsOk = (newComponents) => {
5640
5881
  // src/components/ProcedureForm.tsx
5641
5882
  var import_jsx_runtime32 = require("react/jsx-runtime");
5642
5883
  var ProcedureForm = () => {
5643
- const { procedureName } = (0, import_react_router_dom6.useParams)();
5884
+ const { procedureName } = (0, import_react_router_dom8.useParams)();
5644
5885
  const { clientContext } = useApp();
5645
5886
  const { callApi, loading: apiLoading, error: apiError } = useApiCall();
5646
5887
  const [validationErrors, setValidationErrors] = (0, import_react33.useState)({});
@@ -6058,15 +6299,15 @@ var ProcedureForm = () => {
6058
6299
  var ProcedureForm_default = ProcedureForm;
6059
6300
 
6060
6301
  // src/App.tsx
6061
- var import_react_redux5 = require("react-redux");
6062
- var import_react44 = require("redux-persist/integration/react");
6302
+ var import_react_redux6 = require("react-redux");
6303
+ var import_react45 = require("redux-persist/integration/react");
6063
6304
 
6064
6305
  // src/components/PrivateRoute.tsx
6065
6306
  var import_react34 = require("react");
6066
- var import_react_router_dom7 = require("react-router-dom");
6307
+ var import_react_router_dom9 = require("react-router-dom");
6067
6308
  var import_jsx_runtime33 = require("react/jsx-runtime");
6068
6309
  var PrivateRoute = ({ children }) => {
6069
- const location = (0, import_react_router_dom7.useLocation)();
6310
+ const location = (0, import_react_router_dom9.useLocation)();
6070
6311
  const dispatch = useAppDispatch();
6071
6312
  const clientContextStatus = useClientContextStatus();
6072
6313
  const isAuthenticated = clientContextStatus === "succeeded";
@@ -6074,7 +6315,7 @@ var PrivateRoute = ({ children }) => {
6074
6315
  console.log(`[PrivateRoute] Render: Path=${location.pathname}, AuthStatus=${clientContextStatus}, IsAuthenticated=${isAuthenticated}, IsLoadingAuth=${isLoadingAuth}`);
6075
6316
  (0, import_react34.useEffect)(() => {
6076
6317
  console.log(`[PrivateRoute] Effect: Path=${location.pathname}, AuthStatus=${clientContextStatus}, IsAuthenticated=${isAuthenticated}, IsLoadingAuth=${isLoadingAuth}`);
6077
- if (!isAuthenticated && !isLoadingAuth && location.pathname !== "/login") {
6318
+ if (!isAuthenticated && !isLoadingAuth && location.pathname !== "/login" && !huboCierreVoluntario()) {
6078
6319
  const path = location.pathname + location.search + location.hash;
6079
6320
  console.log(`[PrivateRoute] ACTION: Dispatching setRedirectPath(${path})`);
6080
6321
  dispatch(setRedirectPath(path));
@@ -6092,7 +6333,7 @@ var PrivateRoute = ({ children }) => {
6092
6333
  }
6093
6334
  if (!isAuthenticated) {
6094
6335
  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 });
6336
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_router_dom9.Navigate, { to: "/login", replace: true });
6096
6337
  }
6097
6338
  console.log(`[PrivateRoute] User authenticated, rendering children.`);
6098
6339
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_jsx_runtime33.Fragment, { children });
@@ -6101,13 +6342,15 @@ var PrivateRoute_default = PrivateRoute;
6101
6342
 
6102
6343
  // src/components/InitialRedirectHandler.tsx
6103
6344
  var import_react35 = require("react");
6104
- var import_react_router_dom8 = require("react-router-dom");
6345
+ var import_react_router_dom10 = require("react-router-dom");
6105
6346
  var InitialRedirectHandler = () => {
6106
- const location = (0, import_react_router_dom8.useLocation)();
6107
- const navigate = (0, import_react_router_dom8.useNavigate)();
6347
+ const location = (0, import_react_router_dom10.useLocation)();
6348
+ const navigate = (0, import_react_router_dom10.useNavigate)();
6108
6349
  const dispatch = useAppDispatch();
6109
6350
  const redirectPath = useRedirectPath();
6110
6351
  const clientContextStatus = useClientContextStatus();
6352
+ const username = useAppSelector((state) => state.clientContext.username);
6353
+ const redirectUsername = useAppSelector((state) => state.router.redirectUsername);
6111
6354
  (0, import_react35.useEffect)(() => {
6112
6355
  dispatch(setCurrentPath(location.pathname + location.search));
6113
6356
  }, [location, dispatch]);
@@ -6117,7 +6360,9 @@ var InitialRedirectHandler = () => {
6117
6360
  }, [location.pathname]);
6118
6361
  (0, import_react35.useEffect)(() => {
6119
6362
  if (clientContextStatus === "succeeded") {
6120
- if (redirectPath && redirectPath !== "/login") {
6363
+ if (redirectPath && redirectPath !== "/login" && redirectUsername && redirectUsername !== username) {
6364
+ dispatch(setRedirectPath(null));
6365
+ } else if (redirectPath && redirectPath !== "/login") {
6121
6366
  console.log("Redirigiendo a la ruta guardada:", redirectPath);
6122
6367
  navegandoRef.current = true;
6123
6368
  navigate(redirectPath);
@@ -6127,14 +6372,14 @@ var InitialRedirectHandler = () => {
6127
6372
  navigate("/");
6128
6373
  }
6129
6374
  }
6130
- }, [clientContextStatus, redirectPath, location.pathname, navigate, dispatch]);
6375
+ }, [clientContextStatus, redirectPath, redirectUsername, username, location.pathname, navigate, dispatch]);
6131
6376
  return null;
6132
6377
  };
6133
6378
  var InitialRedirectHandler_default = InitialRedirectHandler;
6134
6379
 
6135
6380
  // src/pages/GenericDataGridPage.tsx
6136
6381
  var import_react36 = __toESM(require("react"), 1);
6137
- var import_react_router_dom9 = require("react-router-dom");
6382
+ var import_react_router_dom11 = require("react-router-dom");
6138
6383
  var import_material27 = require("@mui/material");
6139
6384
  var import_Close2 = __toESM(require("@mui/icons-material/Close"), 1);
6140
6385
  var import_jsx_runtime34 = require("react/jsx-runtime");
@@ -6143,12 +6388,12 @@ var GenericDataGridPage = ({
6143
6388
  initialFixedFields = [],
6144
6389
  parametros: parametrosRecibidos
6145
6390
  }) => {
6146
- const { tableName: urlTableName } = (0, import_react_router_dom9.useParams)();
6391
+ const { tableName: urlTableName } = (0, import_react_router_dom11.useParams)();
6147
6392
  const effectiveTableName = initialTableName || urlTableName;
6148
6393
  const [tabs, setTabs] = (0, import_react36.useState)([]);
6149
6394
  const [activeTabIdx, setActiveTabIdx] = (0, import_react36.useState)(0);
6150
6395
  const ffKey = JSON.stringify(initialFixedFields);
6151
- const location = (0, import_react_router_dom9.useLocation)();
6396
+ const location = (0, import_react_router_dom11.useLocation)();
6152
6397
  const parametros = (0, import_react36.useMemo)(() => parametrosRecibidos ?? parametrosDeTabla(void 0, location.search), [parametrosRecibidos, location.search]);
6153
6398
  const fixedFieldsIniciales = initialFixedFields.length > 0 ? initialFixedFields : parametros.fixedFields ?? [];
6154
6399
  const parametrosKey = JSON.stringify(parametros);
@@ -6229,11 +6474,11 @@ var GenericDataGridPage_default = GenericDataGridPage;
6229
6474
 
6230
6475
  // src/pages/PantallaDeRuta.tsx
6231
6476
  var import_react37 = require("react");
6232
- var import_react_router_dom10 = require("react-router-dom");
6477
+ var import_react_router_dom12 = require("react-router-dom");
6233
6478
  var import_material28 = require("@mui/material");
6234
6479
  var import_jsx_runtime35 = require("react/jsx-runtime");
6235
6480
  var PantallaDeRuta = ({ screenName }) => {
6236
- const [busqueda] = (0, import_react_router_dom10.useSearchParams)();
6481
+ const [busqueda] = (0, import_react_router_dom12.useSearchParams)();
6237
6482
  const params = (0, import_react37.useMemo)(() => Object.fromEntries(busqueda.entries()), [busqueda]);
6238
6483
  const Pantalla = wScreens[screenName];
6239
6484
  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 +6486,13 @@ var PantallaDeRuta = ({ screenName }) => {
6241
6486
 
6242
6487
  // src/pages/MenuTablePage.tsx
6243
6488
  var import_react38 = require("react");
6244
- var import_react_router_dom11 = require("react-router-dom");
6489
+ var import_react_router_dom13 = require("react-router-dom");
6245
6490
  var import_material29 = require("@mui/material");
6246
6491
  var import_jsx_runtime36 = require("react/jsx-runtime");
6247
6492
  var MenuTablePage = () => {
6248
- const { "*": menuName } = (0, import_react_router_dom11.useParams)();
6493
+ const { "*": menuName } = (0, import_react_router_dom13.useParams)();
6249
6494
  const { clientContext } = useApp();
6250
- const { search } = (0, import_react_router_dom11.useLocation)();
6495
+ const { search } = (0, import_react_router_dom13.useLocation)();
6251
6496
  const { currentTableName, currentFixedFields, parametros } = (0, import_react38.useMemo)(() => {
6252
6497
  let tableName = void 0;
6253
6498
  let fixedFields = [];
@@ -6287,7 +6532,7 @@ var MenuTablePage_default = MenuTablePage;
6287
6532
 
6288
6533
  // src/puente/PantallaBP.tsx
6289
6534
  var import_react40 = require("react");
6290
- var import_react_router_dom12 = require("react-router-dom");
6535
+ var import_react_router_dom14 = require("react-router-dom");
6291
6536
  var import_material31 = require("@mui/material");
6292
6537
 
6293
6538
  // src/puente/clienteBP.ts
@@ -6490,13 +6735,6 @@ function resolverPantallaBP(myOwn, nombre, contenedor) {
6490
6735
  return null;
6491
6736
  }
6492
6737
 
6493
- // src/puente/habilitacion.ts
6494
- var habilitado = false;
6495
- var habilitarPuenteBP = () => {
6496
- habilitado = true;
6497
- };
6498
- var puenteBPHabilitado = () => habilitado;
6499
-
6500
6738
  // src/puente/PantallaBP.tsx
6501
6739
  var import_jsx_runtime38 = require("react/jsx-runtime");
6502
6740
  function buscarItemDeMenu(items, tipo) {
@@ -6559,18 +6797,18 @@ var PantallaBP = ({ nombre, direccion }) => {
6559
6797
  );
6560
6798
  };
6561
6799
  var PantallaDeRespaldo = () => {
6562
- const { screenName = "" } = (0, import_react_router_dom12.useParams)();
6563
- const { search } = (0, import_react_router_dom12.useLocation)();
6800
+ const { screenName = "" } = (0, import_react_router_dom14.useParams)();
6801
+ const { search } = (0, import_react_router_dom14.useLocation)();
6564
6802
  if (!puenteBPHabilitado()) {
6565
6803
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material31.Box, { sx: { p: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(FallbackWScreen, { screenName }) });
6566
6804
  }
6567
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_router_dom12.Navigate, { to: rutaDePantallaBP(screenName, search), replace: true });
6805
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_router_dom14.Navigate, { to: rutaDePantallaBP(screenName, search), replace: true });
6568
6806
  };
6569
6807
  var PaginaBP = () => {
6570
- const { hash, search } = (0, import_react_router_dom12.useLocation)();
6808
+ const { hash, search } = (0, import_react_router_dom14.useLocation)();
6571
6809
  const direccion = hash || search;
6572
6810
  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 });
6811
+ if (!puenteBPHabilitado()) return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_router_dom14.Navigate, { to: "/", replace: true });
6574
6812
  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
6813
  "Falta la pantalla de backend-plus (",
6576
6814
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("code", { children: "#w=..." }),
@@ -6581,7 +6819,7 @@ var PaginaBP = () => {
6581
6819
 
6582
6820
  // src/puente/GrillaBP.tsx
6583
6821
  var import_react41 = require("react");
6584
- var import_react_router_dom13 = require("react-router-dom");
6822
+ var import_react_router_dom15 = require("react-router-dom");
6585
6823
  var import_material32 = require("@mui/material");
6586
6824
  var import_jsx_runtime39 = (
6587
6825
  // 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 +6859,13 @@ var GrillaBP = ({ tabla, fixedFields }) => {
6621
6859
  ] });
6622
6860
  };
6623
6861
  var PaginaGrillaBP = () => {
6624
- const { tableName = "" } = (0, import_react_router_dom13.useParams)();
6862
+ const { tableName = "" } = (0, import_react_router_dom15.useParams)();
6625
6863
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GrillaBP, { tabla: tableName }, tableName);
6626
6864
  };
6627
6865
 
6628
6866
  // src/puente/CapturaBP.tsx
6629
6867
  var import_react42 = require("react");
6630
- var import_react_router_dom14 = require("react-router-dom");
6868
+ var import_react_router_dom16 = require("react-router-dom");
6631
6869
 
6632
6870
  // src/puente/capturaDeNavegacion.ts
6633
6871
  function apiDeNavegacion() {
@@ -6672,62 +6910,175 @@ function capturarNavegacionBP(urlBackend, navegar) {
6672
6910
 
6673
6911
  // src/puente/CapturaBP.tsx
6674
6912
  var CapturaBP = () => {
6675
- const navigate = (0, import_react_router_dom14.useNavigate)();
6913
+ const navigate = (0, import_react_router_dom16.useNavigate)();
6676
6914
  (0, import_react42.useEffect)(() => capturarNavegacionBP(envConfig.backendUrl, navigate), [navigate]);
6677
6915
  return null;
6678
6916
  };
6679
6917
 
6918
+ // src/pages/CambiarClavePage.tsx
6919
+ var import_react43 = require("react");
6920
+ var import_material33 = require("@mui/material");
6921
+ var import_react_router_dom17 = require("react-router-dom");
6922
+ var import_react_redux5 = require("react-redux");
6923
+
6924
+ // src/utils/cambiarClave.ts
6925
+ var MENSAJE_POR_OMISION = "No se pudo cambiar la contrase\xF1a";
6926
+ function resultadoDeCambioDeClave(urlFinal, html) {
6927
+ if (/\/login\/?(\?.*)?$/.test(urlFinal)) return { ok: true };
6928
+ const mensajes = Array.from(new DOMParser().parseFromString(html, "text/html").querySelectorAll(".error-message")).map((elemento) => (elemento.textContent ?? "").trim()).filter((mensaje) => mensaje !== "");
6929
+ return { ok: false, mensaje: mensajes.join("\n") || MENSAJE_POR_OMISION };
6930
+ }
6931
+
6932
+ // src/pages/CambiarClavePage.tsx
6933
+ var import_jsx_runtime40 = require("react/jsx-runtime");
6934
+ var CambiarClavePage = () => {
6935
+ const { clientContext, setIsLoggedIn, avisarCierreVoluntario } = useApp();
6936
+ const dispatch = (0, import_react_redux5.useDispatch)();
6937
+ const navigate = (0, import_react_router_dom17.useNavigate)();
6938
+ const [claveActual, setClaveActual] = (0, import_react43.useState)("");
6939
+ const [claveNueva, setClaveNueva] = (0, import_react43.useState)("");
6940
+ const [claveRepetida, setClaveRepetida] = (0, import_react43.useState)("");
6941
+ const [error, setError] = (0, import_react43.useState)(null);
6942
+ const [enviando, setEnviando] = (0, import_react43.useState)(false);
6943
+ const enviar = async (evento) => {
6944
+ evento.preventDefault();
6945
+ setError(null);
6946
+ if (claveNueva !== claveRepetida) {
6947
+ setError("La clave nueva y su repetici\xF3n no coinciden");
6948
+ return;
6949
+ }
6950
+ setEnviando(true);
6951
+ avisarCierreVoluntario(true);
6952
+ try {
6953
+ const respuesta = await fetchApi("/chpass", {
6954
+ method: "POST",
6955
+ body: new URLSearchParams({ oldPassword: claveActual, newPassword: claveNueva, repPassword: claveRepetida })
6956
+ });
6957
+ const resultado = resultadoDeCambioDeClave(respuesta.url, await respuesta.text());
6958
+ if (!resultado.ok) {
6959
+ avisarCierreVoluntario(false);
6960
+ setError(resultado.mensaje);
6961
+ return;
6962
+ }
6963
+ setIsLoggedIn(false);
6964
+ dispatch(clearClientContext());
6965
+ dispatch(setRedirectPath(null));
6966
+ navigate("/login", { replace: true, state: { claveCambiada: true } });
6967
+ } catch (err) {
6968
+ avisarCierreVoluntario(false);
6969
+ setError(mensajeDeError(err, "No se pudo cambiar la contrase\xF1a"));
6970
+ } finally {
6971
+ setEnviando(false);
6972
+ }
6973
+ };
6974
+ 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: [
6975
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.Typography, { variant: "h5", gutterBottom: true, children: "Cambiar contrase\xF1a" }),
6976
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.TextField, { label: "Usuario", value: clientContext?.username ?? "", fullWidth: true, margin: "normal", disabled: true, autoComplete: "username" }),
6977
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
6978
+ import_material33.TextField,
6979
+ {
6980
+ label: "Contrase\xF1a actual",
6981
+ type: "password",
6982
+ value: claveActual,
6983
+ onChange: (e) => setClaveActual(e.target.value),
6984
+ fullWidth: true,
6985
+ margin: "normal",
6986
+ autoFocus: true,
6987
+ required: true,
6988
+ autoComplete: "current-password",
6989
+ disabled: enviando
6990
+ }
6991
+ ),
6992
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
6993
+ import_material33.TextField,
6994
+ {
6995
+ label: "Contrase\xF1a nueva",
6996
+ type: "password",
6997
+ value: claveNueva,
6998
+ onChange: (e) => setClaveNueva(e.target.value),
6999
+ fullWidth: true,
7000
+ margin: "normal",
7001
+ required: true,
7002
+ autoComplete: "new-password",
7003
+ disabled: enviando
7004
+ }
7005
+ ),
7006
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
7007
+ import_material33.TextField,
7008
+ {
7009
+ label: "Repetir contrase\xF1a nueva",
7010
+ type: "password",
7011
+ value: claveRepetida,
7012
+ onChange: (e) => setClaveRepetida(e.target.value),
7013
+ fullWidth: true,
7014
+ margin: "normal",
7015
+ required: true,
7016
+ autoComplete: "new-password",
7017
+ disabled: enviando
7018
+ }
7019
+ ),
7020
+ error && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.Alert, { severity: "error", sx: { mt: 1, whiteSpace: "pre-line" }, children: error }),
7021
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_material33.Box, { sx: { display: "flex", gap: 1, justifyContent: "flex-end", mt: 2 }, children: [
7022
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material33.Button, { onClick: () => navigate(-1), disabled: enviando, children: "Cancelar" }),
7023
+ /* @__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" })
7024
+ ] }),
7025
+ /* @__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." })
7026
+ ] }) });
7027
+ };
7028
+ var CambiarClavePage_default = CambiarClavePage;
7029
+
6680
7030
  // src/App.tsx
6681
7031
  var import_type_store6 = __toESM(require("type-store"), 1);
6682
- var import_jsx_runtime40 = require("react/jsx-runtime");
7032
+ var import_jsx_runtime41 = require("react/jsx-runtime");
6683
7033
  var LocationTracker = () => {
6684
- const location = (0, import_react_router_dom15.useLocation)();
7034
+ const location = (0, import_react_router_dom18.useLocation)();
6685
7035
  const dispatch = useAppDispatch();
6686
- (0, import_react43.useEffect)(() => {
7036
+ (0, import_react44.useEffect)(() => {
6687
7037
  dispatch(setCurrentPath(location.pathname + location.search));
6688
7038
  }, [location, dispatch]);
6689
7039
  return null;
6690
7040
  };
6691
7041
  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, {}) }),
7042
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
7043
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(InitialRedirectHandler_default, {}),
7044
+ puenteBPHabilitado() && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CapturaBP, {}),
7045
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react_router_dom18.Routes, { children: [
7046
+ /* @__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: [
7047
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/login", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(LoginPage_default, {}) }),
7048
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/logout", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(LogoutPage_default, {}) }),
6699
7049
  myUnloggedRoutes
6700
7050
  ] }),
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,
7051
+ /* @__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: [
7052
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PantallaInicial_default, {}) }),
7053
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/table/:tableName", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(GenericDataGridPage_default, {}) }),
7054
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/menu/*", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(MenuTablePage_default, {}) }),
7055
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/procedures/:procedureName", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ProcedureForm_default, {}) }),
7056
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/chpass", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CambiarClavePage_default, {}) }),
7057
+ Object.keys(wScreens).map((screenName) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
7058
+ import_react_router_dom18.Route,
6708
7059
  {
6709
7060
  path: `/wScreens/${screenName}`,
6710
- element: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(PantallaDeRuta, { screenName })
7061
+ element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PantallaDeRuta, { screenName })
6711
7062
  },
6712
7063
  screenName
6713
7064
  )),
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, {}) }),
7065
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/wScreens-fallback/:screenName", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PantallaDeRespaldo, {}) }),
7066
+ puenteBPHabilitado() && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_router_dom18.Route, { path: "/bp/menu", element: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PaginaBP, {}) }),
7067
+ 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
7068
  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" }) })
7069
+ /* @__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
7070
  ] }),
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 }) })
7071
+ /* @__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
7072
  ] })
6722
7073
  ] });
6723
7074
  }
6724
7075
  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, {}),
7076
+ 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: [
7077
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(LocationTracker, {}),
6727
7078
  " ",
6728
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(AppProvider, { publicPaths, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(SnackbarProvider, { children: [
7079
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(AppProvider, { publicPaths, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(SnackbarProvider, { children: [
6729
7080
  children,
6730
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SessionExpiredMessage_default, {})
7081
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(SessionExpiredMessage_default, {})
6731
7082
  ] }) })
6732
7083
  ] }) }) });
6733
7084
  };
@@ -6739,9 +7090,10 @@ var App = ({
6739
7090
  myClientSides,
6740
7091
  myResultsOk,
6741
7092
  myConditions,
6742
- puenteBP
7093
+ puenteBP,
7094
+ recuperarClave
6743
7095
  }) => {
6744
- (0, import_react43.useMemo)(() => {
7096
+ (0, import_react44.useMemo)(() => {
6745
7097
  if (myClientSides) {
6746
7098
  extendClientSides(myClientSides);
6747
7099
  }
@@ -6757,8 +7109,9 @@ var App = ({
6757
7109
  if (puenteBP) {
6758
7110
  habilitarPuenteBP();
6759
7111
  }
6760
- }, [myClientSides, myResultsOk, myWScreens, myConditions, puenteBP]);
6761
- (0, import_react43.useEffect)(() => {
7112
+ configurarRecuperarClave(recuperarClave);
7113
+ }, [myClientSides, myResultsOk, myWScreens, myConditions, puenteBP, recuperarClave]);
7114
+ (0, import_react44.useEffect)(() => {
6762
7115
  import_type_store6.default.messages = import_type_store6.default.i18n.messages.es;
6763
7116
  import_type_store6.default.locale = import_type_store6.default.i18n.locale.es;
6764
7117
  const link = document.querySelector("link[rel*='icon']") || document.createElement("link");
@@ -6767,11 +7120,11 @@ var App = ({
6767
7120
  link.href = `${envConfig.backendUrl}/img/logo-128.png`;
6768
7121
  document.getElementsByTagName("head")[0].appendChild(link);
6769
7122
  }, []);
6770
- const safePaths = import_react43.default.useMemo(() => {
7123
+ const safePaths = import_react44.default.useMemo(() => {
6771
7124
  const extracted = extractPathsFromRoutes(myUnloggedRoutes);
6772
7125
  return ["/login", "/logout", ...extracted];
6773
7126
  }, [myUnloggedRoutes]);
6774
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(FrontendPlusProviders, { publicPaths: safePaths, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
7127
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(FrontendPlusProviders, { publicPaths: safePaths, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
6775
7128
  FrontendPlusReactRoutes,
6776
7129
  {
6777
7130
  myRoutes,