frontend-plus-react 0.4.1 → 0.4.2

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/CLAUDE.md CHANGED
@@ -94,8 +94,9 @@ Para no perder lo que la grilla de `backend-plus` ya hace y que acá todavía no
94
94
  - Tres cosas que el cliente de `backend-plus` necesita y que el puente hace: un `<base href>` en la base-url (arma sus direcciones relativas a la página), el global `my` igual a `myOwn`, y llamar a `myOwn.autoSetup()` a mano (él lo hace al evento `load`, que ya pasó).
95
95
  - Una wScreen de `backend-plus` puede ser una función, un objeto con `mainFunction`, o un objeto con `parameters` y `mainAction` (como `abrir_encuesta` de dmencu: muestra el formulario de parámetros). `resolverPantallaBP` reproduce el `showPage` de my-menu.js para las tres.
96
96
  - **Las pantallas de `backend-plus` viven en una sola ruta, `/bp/menu#w=<pantalla>&...`** (`PaginaBP`; `/wScreens-fallback/:nombre` redirige ahí). Se parece a `<base-url>/menu` **a propósito**: BP y las aplicaciones arman direcciones a partir de la ubicación (`location.origin + location.pathname + '/../menu#w=...'` en un `history.replaceState` + `location.reload()`, como el «Cerrar» de una encuesta de dmencu; `location.pathname + '#w=...'` para abrir en otra pestaña) y así caen en esta misma ruta, que lee los parámetros del hash (o de la query) como BP. Con otra ruta, esas direcciones salen de `/react`. En producción, el servidor tiene que devolver el `index.html` también para `/react/bp/menu`.
97
- - **Lo que BP manda a páginas del backend se resuelve adentro** (`capturaDeNavegacion.ts`, activo mientras hay una pantalla de BP): clics en `<a>` (a nivel documento, también en diálogos) y, donde existe la Navigation API, cualquier `location.href = ...`. `rutaDeEnlaceBP` traduce `<base-url>/menu#w=...` a `/bp/menu#w=...`, y `login`/`logout` a los de la aplicación. El clic se corta con `stopPropagation`: el botón «proceder» de BP tiene además un `onclick` propio (`history.pushState` a `<base-url>/menu#...` y `showPage`) que dejaría la URL fuera de la aplicación y daría un error de BP. También se traduce `history.replaceState`: al editar un parámetro de una pantalla (`myOwn.replaceAddrParams`) BP reescribe la dirección a `menu#...` y, con el `<base>`, la aplicación quedaba en `<base-url>/menu` (un F5 y se salía de `/react`); `replaceState` no pasa por la Navigation API. Los enlaces `menu#i=...` (ítems del menú de BP, sin `w`) no se traducen. Los parámetros se leen con `myOwn.UriSearchToObject`, no con `URLSearchParams`: `up`, `ff` y `detailing` tienen su propio formato y llegan como objetos.
97
+ - **Lo que BP manda a páginas del backend se resuelve adentro** (`capturaDeNavegacion.ts`, montado una sola vez por `CapturaBP` mientras el puente está encendido, **no** solo mientras hay una pantalla de BP: su cliente sigue vivo después de salir de ella y sus avisos quedan en el `body`; por ejemplo el de «Iniciar sesión» de `my-inform-net-status.js`, un `<a href="login">` que sin la captura llevaba a `<base-url>/login` y, con la sesión abierta, a `<base-url>/menu`, el BP nativo): clics en `<a>` (a nivel documento, también en diálogos) y, donde existe la Navigation API, cualquier `location.href = ...`. `rutaDeEnlaceBP` traduce `<base-url>/menu#w=...` a `/bp/menu#w=...`, y `login`/`logout` a los de la aplicación. El clic se corta con `stopPropagation`: el botón «proceder» de BP tiene además un `onclick` propio (`history.pushState` a `<base-url>/menu#...` y `showPage`) que dejaría la URL fuera de la aplicación y daría un error de BP. También se traduce `history.replaceState`: al editar un parámetro de una pantalla (`myOwn.replaceAddrParams`) BP reescribe la dirección a `menu#...` y, con el `<base>`, la aplicación quedaba en `<base-url>/menu` (un F5 y se salía de `/react`); `replaceState` no pasa por la Navigation API. Los enlaces `menu#i=...` (ítems del menú de BP, sin `w`) no se traducen. Los parámetros se leen con `myOwn.UriSearchToObject`, no con `URLSearchParams`: `up`, `ff` y `detailing` tienen su propio formato y llegan como objetos.
98
98
  - **Los scripts de `backend-plus` se inyectan recién cuando la ventana terminó de cargar** (`esperarCargaDeVentana`): su `load` ejecuta `showPage`, que acá falla (no hay `#total-layout`). Y su manejador de errores (`myOwn.log`) deja el mensaje como primer hijo del `body` cuando no hay un `#mini-console`, lo que **corre toda la aplicación hacia abajo** durante 30 s. Era el «espacio entre el menú y el contenido» que aparecía con F5 y a veces no.
99
+ - **La aplicación no está atada a la ruta `/react`.** `my-menu.js` escucha `popstate` y, si la dirección no termina en `/react`, ejecuta `showPage()` (falla igual que el `load`); lo marca como `//TODO: desacoplar de BP`. El puente lo evita sin tocar `backend-plus`: mientras inyecta los scripts, `sinEscuchasDeVentana(['popstate'], ...)` ignora que registren ese evento (la navegación es del router). Si backend-plus agrega otro enganche así, se ve como un error en `ConsolaBP`. Sin el puente (`puenteBP` apagado) no se carga nada de `backend-plus` y la base-url de la aplicación es la que quiera. **Sin probar en navegador** (se probó el filtro en Vitest; falta atrás/adelante contra un backend real).
99
100
  - **`ConsolaBP`** ofrece ese `#mini-console` (dentro de `PantallaBP` y `GrillaBP`): los mensajes y errores de `backend-plus` se ven como avisos flotantes arriba a la derecha, debajo de la AppBar, sin ocupar lugar. BP registra algunos errores **dos veces** (el de una llamada ajax lo registra `my-ajax.js` y el `catch` de «proceder», en `my-menu.js`, lo repite; pasa también en BP nativo): si llega un aviso igual a uno que llegó hace menos de 1 s, se descarta (`descartarRepetidos`). Sin contador: el error se ve una vez. Las hojas de estilo del backend y de las aplicaciones también estilizan `#mini-console` (por id, con `top`, `width: 100%`, `max-height`...): por eso `ConsolaBP` usa `&#mini-console` y repone esos valores.
100
101
  - Las wScreens de `backend-plus` dibujan en `#main_layout`: `PantallaBP` le da ese contenedor y le pasa lo que declara su ítem de menú más la query string. Mientras está montada no puede haber otro elemento con ese id.
101
102
  - `rutaBase` (`src/rutaBase.ts`) toma el `base` del bundler: es el `basename` del router y el prefijo de las pestañas nuevas (Ctrl+click en un detalle `wScreen`).
package/dist/index.cjs CHANGED
@@ -4190,7 +4190,7 @@ var GenericDataGrid = ({
4190
4190
  var GenericDataGrid_default = GenericDataGrid;
4191
4191
 
4192
4192
  // src/App.tsx
4193
- var import_react38 = __toESM(require("react"), 1);
4193
+ var import_react39 = __toESM(require("react"), 1);
4194
4194
  var import_react_router_dom15 = require("react-router-dom");
4195
4195
 
4196
4196
  // src/components/MainLayout.tsx
@@ -4759,7 +4759,6 @@ var import_material20 = require("@mui/material");
4759
4759
  var import_react_hook_form = require("react-hook-form");
4760
4760
  var import_zod = require("@hookform/resolvers/zod");
4761
4761
  var z = __toESM(require("zod"), 1);
4762
- var import_react_router_dom6 = require("react-router-dom");
4763
4762
  var import_react_redux4 = require("react-redux");
4764
4763
  var import_jsx_runtime25 = require("react/jsx-runtime");
4765
4764
  var loginSchema = z.object({
@@ -4769,7 +4768,6 @@ var loginSchema = z.object({
4769
4768
  // Añadir validación básica
4770
4769
  });
4771
4770
  var LoginPage = () => {
4772
- const navigate = (0, import_react_router_dom6.useNavigate)();
4773
4771
  const [loginError, setLoginError] = (0, import_react28.useState)(null);
4774
4772
  const [isSubmitting, setIsSubmitting] = (0, import_react28.useState)(false);
4775
4773
  const { setIsLoggedIn, checkSession } = useApp();
@@ -4807,7 +4805,6 @@ var LoginPage = () => {
4807
4805
  setIsLoggedIn(true);
4808
4806
  await dispatch(fetchClientContext()).unwrap();
4809
4807
  console.log("Login exitoso y clientContext cargado.");
4810
- navigate("/");
4811
4808
  reset();
4812
4809
  } catch (error) {
4813
4810
  console.error("Error al iniciar sesi\xF3n:", error);
@@ -4911,7 +4908,7 @@ var LoginPage_default = LoginPage;
4911
4908
 
4912
4909
  // src/components/ProcedureForm.tsx
4913
4910
  var import_react29 = __toESM(require("react"), 1);
4914
- var import_react_router_dom7 = require("react-router-dom");
4911
+ var import_react_router_dom6 = require("react-router-dom");
4915
4912
  var import_material22 = require("@mui/material");
4916
4913
 
4917
4914
  // src/pages/procedure-results/SuccessDisplay.tsx
@@ -4938,7 +4935,7 @@ var extendResultsOk = (newComponents) => {
4938
4935
  // src/components/ProcedureForm.tsx
4939
4936
  var import_jsx_runtime27 = require("react/jsx-runtime");
4940
4937
  var ProcedureForm = () => {
4941
- const { procedureName } = (0, import_react_router_dom7.useParams)();
4938
+ const { procedureName } = (0, import_react_router_dom6.useParams)();
4942
4939
  const { clientContext } = useApp();
4943
4940
  const { callApi, loading: apiLoading, error: apiError } = useApiCall();
4944
4941
  const [validationErrors, setValidationErrors] = (0, import_react29.useState)({});
@@ -5357,14 +5354,14 @@ var ProcedureForm_default = ProcedureForm;
5357
5354
 
5358
5355
  // src/App.tsx
5359
5356
  var import_react_redux5 = require("react-redux");
5360
- var import_react39 = require("redux-persist/integration/react");
5357
+ var import_react40 = require("redux-persist/integration/react");
5361
5358
 
5362
5359
  // src/components/PrivateRoute.tsx
5363
5360
  var import_react30 = require("react");
5364
- var import_react_router_dom8 = require("react-router-dom");
5361
+ var import_react_router_dom7 = require("react-router-dom");
5365
5362
  var import_jsx_runtime28 = require("react/jsx-runtime");
5366
5363
  var PrivateRoute = ({ children }) => {
5367
- const location = (0, import_react_router_dom8.useLocation)();
5364
+ const location = (0, import_react_router_dom7.useLocation)();
5368
5365
  const dispatch = useAppDispatch();
5369
5366
  const clientContextStatus = useClientContextStatus();
5370
5367
  const isAuthenticated = clientContextStatus === "succeeded";
@@ -5390,7 +5387,7 @@ var PrivateRoute = ({ children }) => {
5390
5387
  }
5391
5388
  if (!isAuthenticated) {
5392
5389
  console.log(`[PrivateRoute] User not authenticated, navigating to /login`);
5393
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_router_dom8.Navigate, { to: "/login", replace: true });
5390
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_router_dom7.Navigate, { to: "/login", replace: true });
5394
5391
  }
5395
5392
  console.log(`[PrivateRoute] User authenticated, rendering children.`);
5396
5393
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_jsx_runtime28.Fragment, { children });
@@ -5399,10 +5396,10 @@ var PrivateRoute_default = PrivateRoute;
5399
5396
 
5400
5397
  // src/components/InitialRedirectHandler.tsx
5401
5398
  var import_react31 = require("react");
5402
- var import_react_router_dom9 = require("react-router-dom");
5399
+ var import_react_router_dom8 = require("react-router-dom");
5403
5400
  var InitialRedirectHandler = () => {
5404
- const location = (0, import_react_router_dom9.useLocation)();
5405
- const navigate = (0, import_react_router_dom9.useNavigate)();
5401
+ const location = (0, import_react_router_dom8.useLocation)();
5402
+ const navigate = (0, import_react_router_dom8.useNavigate)();
5406
5403
  const dispatch = useAppDispatch();
5407
5404
  const redirectPath = useRedirectPath();
5408
5405
  const clientContextStatus = useClientContextStatus();
@@ -5432,7 +5429,7 @@ var InitialRedirectHandler_default = InitialRedirectHandler;
5432
5429
 
5433
5430
  // src/pages/GenericDataGridPage.tsx
5434
5431
  var import_react32 = __toESM(require("react"), 1);
5435
- var import_react_router_dom10 = require("react-router-dom");
5432
+ var import_react_router_dom9 = require("react-router-dom");
5436
5433
  var import_material23 = require("@mui/material");
5437
5434
  var import_Close2 = __toESM(require("@mui/icons-material/Close"), 1);
5438
5435
  var import_jsx_runtime29 = require("react/jsx-runtime");
@@ -5440,12 +5437,12 @@ var GenericDataGridPage = ({
5440
5437
  initialTableName,
5441
5438
  initialFixedFields = []
5442
5439
  }) => {
5443
- const { tableName: urlTableName } = (0, import_react_router_dom10.useParams)();
5440
+ const { tableName: urlTableName } = (0, import_react_router_dom9.useParams)();
5444
5441
  const effectiveTableName = initialTableName || urlTableName;
5445
5442
  const [tabs, setTabs] = (0, import_react32.useState)([]);
5446
5443
  const [activeTabIdx, setActiveTabIdx] = (0, import_react32.useState)(0);
5447
5444
  const ffKey = JSON.stringify(initialFixedFields);
5448
- const location = (0, import_react_router_dom10.useLocation)();
5445
+ const location = (0, import_react_router_dom9.useLocation)();
5449
5446
  import_react32.default.useEffect(() => {
5450
5447
  if (effectiveTableName) {
5451
5448
  setTabs([{
@@ -5518,11 +5515,11 @@ var GenericDataGridPage_default = GenericDataGridPage;
5518
5515
 
5519
5516
  // src/pages/PantallaDeRuta.tsx
5520
5517
  var import_react33 = require("react");
5521
- var import_react_router_dom11 = require("react-router-dom");
5518
+ var import_react_router_dom10 = require("react-router-dom");
5522
5519
  var import_material24 = require("@mui/material");
5523
5520
  var import_jsx_runtime30 = require("react/jsx-runtime");
5524
5521
  var PantallaDeRuta = ({ screenName }) => {
5525
- const [busqueda] = (0, import_react_router_dom11.useSearchParams)();
5522
+ const [busqueda] = (0, import_react_router_dom10.useSearchParams)();
5526
5523
  const params = (0, import_react33.useMemo)(() => Object.fromEntries(busqueda.entries()), [busqueda]);
5527
5524
  const Pantalla = wScreens[screenName];
5528
5525
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_material24.Box, { sx: { pt: 3, pl: 4 }, children: Pantalla ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Pantalla, { screenName, params }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(FallbackWScreen, { screenName }) });
@@ -5530,7 +5527,7 @@ var PantallaDeRuta = ({ screenName }) => {
5530
5527
 
5531
5528
  // src/pages/MenuTablePage.tsx
5532
5529
  var import_react34 = require("react");
5533
- var import_react_router_dom12 = require("react-router-dom");
5530
+ var import_react_router_dom11 = require("react-router-dom");
5534
5531
  var import_material25 = require("@mui/material");
5535
5532
  var import_jsx_runtime31 = require("react/jsx-runtime");
5536
5533
  function fixedFieldsDeMenu(ff) {
@@ -5541,7 +5538,7 @@ function fixedFieldsDeMenu(ff) {
5541
5538
  return Object.entries(ff).map(([fieldName, value]) => ({ fieldName, value }));
5542
5539
  }
5543
5540
  var MenuTablePage = () => {
5544
- const { menuName } = (0, import_react_router_dom12.useParams)();
5541
+ const { menuName } = (0, import_react_router_dom11.useParams)();
5545
5542
  const { clientContext } = useApp();
5546
5543
  const findMenuItem = (items, nameToFind) => {
5547
5544
  for (const item of items) {
@@ -5590,7 +5587,7 @@ var MenuTablePage_default = MenuTablePage;
5590
5587
 
5591
5588
  // src/puente/PantallaBP.tsx
5592
5589
  var import_react36 = require("react");
5593
- var import_react_router_dom13 = require("react-router-dom");
5590
+ var import_react_router_dom12 = require("react-router-dom");
5594
5591
  var import_material27 = require("@mui/material");
5595
5592
 
5596
5593
  // src/puente/clienteBP.ts
@@ -5625,6 +5622,24 @@ function esperarCargaDeVentana() {
5625
5622
  if (document.readyState === "complete") return Promise.resolve();
5626
5623
  return new Promise((resolver) => window.addEventListener("load", () => resolver(), { once: true }));
5627
5624
  }
5625
+ async function sinEscuchasDeVentana(tipos, inyectar) {
5626
+ const original = window.addEventListener;
5627
+ const filtrada = function(tipo, escucha, opciones) {
5628
+ if (tipos.includes(tipo)) return;
5629
+ original.call(this, tipo, escucha, opciones);
5630
+ };
5631
+ window.addEventListener = filtrada;
5632
+ try {
5633
+ return await inyectar();
5634
+ } finally {
5635
+ if (window.addEventListener === filtrada) window.addEventListener = original;
5636
+ }
5637
+ }
5638
+ async function cargarScripts(rutas) {
5639
+ for (const ruta of rutas) {
5640
+ await cargarScript(ruta);
5641
+ }
5642
+ }
5628
5643
  var carga = null;
5629
5644
  function cargarClienteBP(urlBackend) {
5630
5645
  if (!carga) {
@@ -5653,9 +5668,7 @@ async function cargarSinMemoria(urlBackend) {
5653
5668
  document.head.appendChild(enlace);
5654
5669
  });
5655
5670
  await esperarCargaDeVentana();
5656
- for (const ruta of recursos.scripts) {
5657
- await cargarScript(ruta);
5658
- }
5671
+ await sinEscuchasDeVentana(["popstate"], () => cargarScripts(recursos.scripts));
5659
5672
  const myOwn = window.myOwn;
5660
5673
  if (!myOwn) throw new Error("El backend no defini\xF3 myOwn.");
5661
5674
  window.my = myOwn;
@@ -5777,47 +5790,6 @@ function resolverPantallaBP(myOwn, nombre, contenedor) {
5777
5790
  return null;
5778
5791
  }
5779
5792
 
5780
- // src/puente/capturaDeNavegacion.ts
5781
- function apiDeNavegacion() {
5782
- const api = Reflect.get(window, "navigation");
5783
- return typeof api === "object" && api !== null && "addEventListener" in api && typeof api.addEventListener === "function" ? api : null;
5784
- }
5785
- function capturarNavegacionBP(urlBackend, navegar) {
5786
- const alHacerClick = (evento) => {
5787
- if (evento.defaultPrevented || evento.button !== 0 || evento.ctrlKey || evento.metaKey || evento.shiftKey) return;
5788
- const enlace = evento.target instanceof Element ? evento.target.closest("a[href]") : null;
5789
- if (!enlace || enlace.hasAttribute("download") || (enlace.getAttribute("target") ?? "_self") !== "_self") return;
5790
- const ruta = rutaDeEnlaceBP(enlace.getAttribute("href") ?? "", document.baseURI, urlBackend);
5791
- if (ruta) {
5792
- evento.preventDefault();
5793
- evento.stopPropagation();
5794
- navegar(ruta);
5795
- }
5796
- };
5797
- document.addEventListener("click", alHacerClick, true);
5798
- const navegacion = apiDeNavegacion();
5799
- const alNavegar = (evento) => {
5800
- if (!evento.cancelable || evento.destination.sameDocument || evento.downloadRequest !== null) return;
5801
- const ruta = rutaDeEnlaceBP(evento.destination.url, document.baseURI, urlBackend);
5802
- if (ruta) {
5803
- evento.preventDefault();
5804
- navegar(ruta);
5805
- }
5806
- };
5807
- navegacion?.addEventListener("navigate", alNavegar);
5808
- const reemplazarOriginal = history.replaceState;
5809
- const reemplazar = (estado, titulo, url) => {
5810
- const ruta = url === void 0 || url === null ? null : rutaDeEnlaceBP(String(url), document.baseURI, urlBackend);
5811
- reemplazarOriginal.call(history, estado, titulo, ruta === null ? url : rutaBase + ruta);
5812
- };
5813
- history.replaceState = reemplazar;
5814
- return () => {
5815
- if (history.replaceState === reemplazar) history.replaceState = reemplazarOriginal;
5816
- document.removeEventListener("click", alHacerClick, true);
5817
- navegacion?.removeEventListener("navigate", alNavegar);
5818
- };
5819
- }
5820
-
5821
5793
  // src/puente/habilitacion.ts
5822
5794
  var habilitado = false;
5823
5795
  var habilitarPuenteBP = () => {
@@ -5838,7 +5810,6 @@ function buscarItemDeMenu(items, tipo) {
5838
5810
  return void 0;
5839
5811
  }
5840
5812
  var PantallaBP = ({ nombre, direccion }) => {
5841
- const navigate = (0, import_react_router_dom13.useNavigate)();
5842
5813
  const { clientContext } = useApp();
5843
5814
  const [cargando, setCargando] = (0, import_react36.useState)(true);
5844
5815
  const [error, setError] = (0, import_react36.useState)(null);
@@ -5852,7 +5823,6 @@ var PantallaBP = ({ nombre, direccion }) => {
5852
5823
  setError(null);
5853
5824
  setNoExiste(false);
5854
5825
  setCargando(true);
5855
- const dejarDeCapturar = capturarNavegacionBP(envConfig.backendUrl, navigate);
5856
5826
  cargarClienteBP(envConfig.backendUrl).then((myOwn) => {
5857
5827
  if (!vigente) return;
5858
5828
  setCargando(false);
@@ -5874,10 +5844,9 @@ var PantallaBP = ({ nombre, direccion }) => {
5874
5844
  });
5875
5845
  return () => {
5876
5846
  vigente = false;
5877
- dejarDeCapturar();
5878
5847
  elemento.replaceChildren();
5879
5848
  };
5880
- }, [nombre, direccion, menu, navigate]);
5849
+ }, [nombre, direccion, menu]);
5881
5850
  if (noExiste) return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(FallbackWScreen, { screenName: nombre });
5882
5851
  return (
5883
5852
  // 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
@@ -5890,18 +5859,18 @@ var PantallaBP = ({ nombre, direccion }) => {
5890
5859
  );
5891
5860
  };
5892
5861
  var PantallaDeRespaldo = () => {
5893
- const { screenName = "" } = (0, import_react_router_dom13.useParams)();
5894
- const { search } = (0, import_react_router_dom13.useLocation)();
5862
+ const { screenName = "" } = (0, import_react_router_dom12.useParams)();
5863
+ const { search } = (0, import_react_router_dom12.useLocation)();
5895
5864
  if (!puenteBPHabilitado()) {
5896
5865
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_material27.Box, { sx: { p: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(FallbackWScreen, { screenName }) });
5897
5866
  }
5898
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_router_dom13.Navigate, { to: rutaDePantallaBP(screenName, search), replace: true });
5867
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_router_dom12.Navigate, { to: rutaDePantallaBP(screenName, search), replace: true });
5899
5868
  };
5900
5869
  var PaginaBP = () => {
5901
- const { hash, search } = (0, import_react_router_dom13.useLocation)();
5870
+ const { hash, search } = (0, import_react_router_dom12.useLocation)();
5902
5871
  const direccion = hash || search;
5903
5872
  const nombre = new URLSearchParams(direccion.replace(/^[#?]/, "")).get("w");
5904
- if (!puenteBPHabilitado()) return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_router_dom13.Navigate, { to: "/", replace: true });
5873
+ if (!puenteBPHabilitado()) return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_router_dom12.Navigate, { to: "/", replace: true });
5905
5874
  if (!nombre) return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_material27.Box, { sx: { p: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_material27.Alert, { severity: "warning", children: [
5906
5875
  "Falta la pantalla de backend-plus (",
5907
5876
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("code", { children: "#w=..." }),
@@ -5912,7 +5881,7 @@ var PaginaBP = () => {
5912
5881
 
5913
5882
  // src/puente/GrillaBP.tsx
5914
5883
  var import_react37 = require("react");
5915
- var import_react_router_dom14 = require("react-router-dom");
5884
+ var import_react_router_dom13 = require("react-router-dom");
5916
5885
  var import_material28 = require("@mui/material");
5917
5886
  var import_jsx_runtime34 = (
5918
5887
  // 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
@@ -5952,17 +5921,69 @@ var GrillaBP = ({ tabla, fixedFields }) => {
5952
5921
  ] });
5953
5922
  };
5954
5923
  var PaginaGrillaBP = () => {
5955
- const { tableName = "" } = (0, import_react_router_dom14.useParams)();
5924
+ const { tableName = "" } = (0, import_react_router_dom13.useParams)();
5956
5925
  return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(GrillaBP, { tabla: tableName }, tableName);
5957
5926
  };
5958
5927
 
5928
+ // src/puente/CapturaBP.tsx
5929
+ var import_react38 = require("react");
5930
+ var import_react_router_dom14 = require("react-router-dom");
5931
+
5932
+ // src/puente/capturaDeNavegacion.ts
5933
+ function apiDeNavegacion() {
5934
+ const api = Reflect.get(window, "navigation");
5935
+ return typeof api === "object" && api !== null && "addEventListener" in api && typeof api.addEventListener === "function" ? api : null;
5936
+ }
5937
+ function capturarNavegacionBP(urlBackend, navegar) {
5938
+ const alHacerClick = (evento) => {
5939
+ if (evento.defaultPrevented || evento.button !== 0 || evento.ctrlKey || evento.metaKey || evento.shiftKey) return;
5940
+ const enlace = evento.target instanceof Element ? evento.target.closest("a[href]") : null;
5941
+ if (!enlace || enlace.hasAttribute("download") || (enlace.getAttribute("target") ?? "_self") !== "_self") return;
5942
+ const ruta = rutaDeEnlaceBP(enlace.getAttribute("href") ?? "", document.baseURI, urlBackend);
5943
+ if (ruta) {
5944
+ evento.preventDefault();
5945
+ evento.stopPropagation();
5946
+ navegar(ruta);
5947
+ }
5948
+ };
5949
+ document.addEventListener("click", alHacerClick, true);
5950
+ const navegacion = apiDeNavegacion();
5951
+ const alNavegar = (evento) => {
5952
+ if (!evento.cancelable || evento.destination.sameDocument || evento.downloadRequest !== null) return;
5953
+ const ruta = rutaDeEnlaceBP(evento.destination.url, document.baseURI, urlBackend);
5954
+ if (ruta) {
5955
+ evento.preventDefault();
5956
+ navegar(ruta);
5957
+ }
5958
+ };
5959
+ navegacion?.addEventListener("navigate", alNavegar);
5960
+ const reemplazarOriginal = history.replaceState;
5961
+ const reemplazar = (estado, titulo, url) => {
5962
+ const ruta = url === void 0 || url === null ? null : rutaDeEnlaceBP(String(url), document.baseURI, urlBackend);
5963
+ reemplazarOriginal.call(history, estado, titulo, ruta === null ? url : rutaBase + ruta);
5964
+ };
5965
+ history.replaceState = reemplazar;
5966
+ return () => {
5967
+ if (history.replaceState === reemplazar) history.replaceState = reemplazarOriginal;
5968
+ document.removeEventListener("click", alHacerClick, true);
5969
+ navegacion?.removeEventListener("navigate", alNavegar);
5970
+ };
5971
+ }
5972
+
5973
+ // src/puente/CapturaBP.tsx
5974
+ var CapturaBP = () => {
5975
+ const navigate = (0, import_react_router_dom14.useNavigate)();
5976
+ (0, import_react38.useEffect)(() => capturarNavegacionBP(envConfig.backendUrl, navigate), [navigate]);
5977
+ return null;
5978
+ };
5979
+
5959
5980
  // src/App.tsx
5960
5981
  var import_type_store6 = __toESM(require("type-store"), 1);
5961
5982
  var import_jsx_runtime35 = require("react/jsx-runtime");
5962
5983
  var LocationTracker = () => {
5963
5984
  const location = (0, import_react_router_dom15.useLocation)();
5964
5985
  const dispatch = useAppDispatch();
5965
- (0, import_react38.useEffect)(() => {
5986
+ (0, import_react39.useEffect)(() => {
5966
5987
  dispatch(setCurrentPath(location.pathname + location.search));
5967
5988
  }, [location, dispatch]);
5968
5989
  return null;
@@ -5970,6 +5991,7 @@ var LocationTracker = () => {
5970
5991
  function FrontendPlusReactRoutes({ myRoutes, myUnloggedRoutes, layout: Layout = MainLayout_default, unloggedLayout: UnloggedLayout }) {
5971
5992
  return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
5972
5993
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(InitialRedirectHandler_default, {}),
5994
+ puenteBPHabilitado() && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(CapturaBP, {}),
5973
5995
  /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_react_router_dom15.Routes, { children: [
5974
5996
  /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_react_router_dom15.Route, { element: UnloggedLayout ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(UnloggedLayout, {}) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_router_dom15.Outlet, {}), children: [
5975
5997
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_router_dom15.Route, { path: "/login", element: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(LoginPage_default, {}) }),
@@ -6000,7 +6022,7 @@ function FrontendPlusReactRoutes({ myRoutes, myUnloggedRoutes, layout: Layout =
6000
6022
  ] });
6001
6023
  }
6002
6024
  var FrontendPlusProviders = ({ children, publicPaths }) => {
6003
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_redux5.Provider, { store, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react39.PersistGate, { loading: null, persistor, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_react_router_dom15.BrowserRouter, { basename: rutaBase, children: [
6025
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_redux5.Provider, { store, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react40.PersistGate, { loading: null, persistor, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_react_router_dom15.BrowserRouter, { basename: rutaBase, children: [
6004
6026
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(LocationTracker, {}),
6005
6027
  " ",
6006
6028
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(AppProvider, { publicPaths, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(SnackbarProvider, { children: [
@@ -6019,7 +6041,7 @@ var App = ({
6019
6041
  myConditions,
6020
6042
  puenteBP
6021
6043
  }) => {
6022
- (0, import_react38.useMemo)(() => {
6044
+ (0, import_react39.useMemo)(() => {
6023
6045
  if (myClientSides) {
6024
6046
  extendClientSides(myClientSides);
6025
6047
  }
@@ -6036,7 +6058,7 @@ var App = ({
6036
6058
  habilitarPuenteBP();
6037
6059
  }
6038
6060
  }, [myClientSides, myResultsOk, myWScreens, myConditions, puenteBP]);
6039
- (0, import_react38.useEffect)(() => {
6061
+ (0, import_react39.useEffect)(() => {
6040
6062
  import_type_store6.default.messages = import_type_store6.default.i18n.messages.es;
6041
6063
  import_type_store6.default.locale = import_type_store6.default.i18n.locale.es;
6042
6064
  const link = document.querySelector("link[rel*='icon']") || document.createElement("link");
@@ -6045,7 +6067,7 @@ var App = ({
6045
6067
  link.href = `${envConfig.backendUrl}/img/logo-128.png`;
6046
6068
  document.getElementsByTagName("head")[0].appendChild(link);
6047
6069
  }, []);
6048
- const safePaths = import_react38.default.useMemo(() => {
6070
+ const safePaths = import_react39.default.useMemo(() => {
6049
6071
  const extracted = extractPathsFromRoutes(myUnloggedRoutes);
6050
6072
  return ["/login", "/logout", ...extracted];
6051
6073
  }, [myUnloggedRoutes]);