frontend-plus-react 0.3.0 → 0.4.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/CLAUDE.md +153 -0
- package/dist/index.cjs +323 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +317 -39
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# CLAUDE.md — frontend-plus-react
|
|
2
|
+
|
|
3
|
+
Librería React (MUI + react-data-grid) para consumir backends hechos con `backend-plus` sin reescribir el frontend de cada aplicación: login, menú, grillas editables de cualquier tabla (con sus detalles anidados), formularios de procedimientos y editor vertical. Cada aplicación aporta solo lo propio (wScreens, clientSides, resultados de procedimientos) mediante `extendWScreens`, `extendClientSides` y `extendResultsOk`.
|
|
4
|
+
|
|
5
|
+
Se consume desde las aplicaciones (`mi-frontend`, `dmencu-frontend`, y las que vengan) como paquete npm o enlazado con `npm link`. Esta librería **no** es el frontend de `backend-plus` (ese es el que sirve el propio framework con `my-tables.js`): reproduce su comportamiento. Ante la duda de cómo debe comportarse algo, la referencia es `backend-plus` (`for-client/my-tables.js`, `unlogged/my-ajax.js`) y los `.d.ts` en `node_modules/backend-plus/lib/backend-plus.d.ts`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Reglas de código
|
|
10
|
+
|
|
11
|
+
Son las de `dmencu` (que aplican a todo proyecto compartido de esta carpeta), escritas acá porque esta librería no depende de `dmencu` y no puede importar su `CLAUDE.md`:
|
|
12
|
+
|
|
13
|
+
- **Todos los tipos explícitos.** Prohibido `any`, `@ts-ignore`, casts vía `unknown` o magia sin justificación. Hoy el código no tiene ninguno y tiene que seguir así. Si un callback recibe un parámetro, tiparlo.
|
|
14
|
+
- Lo que viene del servidor o de un catch es `unknown`: `mensajeDeError(err)` para el mensaje de un error y `requerido(valor, 'qué faltó')` para algo que tiene que existir (ambos en `utils/errores.ts`). Las filas son `FilaDeDatos`; las filas de resumen (filtros y totales), `FilaDeResumen`; los parámetros de un procedimiento, `ParametrosDeProcedimiento`.
|
|
15
|
+
- `callApi<R>(...)` lleva la forma del resultado de ese procedimiento; la única afirmación de tipo sobre lo que manda el servidor está en `resultadoDeProcedimiento` (`utils/fetchApi.ts`).
|
|
16
|
+
- `type-store` no publica tipos: están declarados en `src/types/type-store.d.ts`, escritos para mudarse tal cual a `types.d.ts` (`modules/type-store/index.d.ts`). Las columnas propias se distinguen con `esColumnaPropia` y `customType`, no con casts.
|
|
17
|
+
- Una excepción acotada: los tests usan dobles de los eventos de react-data-grid con una aserción de tipo, en un único helper por archivo.
|
|
18
|
+
- Si una `interface` no cabe en `FilaDeDatos` (`Record<string, unknown>`), es porque las interfaces no tienen firma de índice: usar `type`.
|
|
19
|
+
- **Preservar el estilo existente** y **no tocar el formato de lo que ya existe**: el diff muestra solo las líneas que cambian de verdad. Sin reindentar, sin tocar espacios finales, sin agregar ni quitar el salto de línea final.
|
|
20
|
+
- **Respetar el fin de línea de cada archivo**: hay archivos con CRLF y otros con LF. Las líneas nuevas llevan el mismo que el resto del archivo (`git ls-files --eol <archivo>`). No editar con `sed -i` ni con herramientas que reescriban el archivo entero. Antes de commitear, `git diff --stat` tiene que coincidir con `git diff --ignore-all-space --ignore-cr-at-eol --stat`; si no, hay formato que revertir.
|
|
21
|
+
- **Nombres de dominio, funciones, variables y comentarios en castellano** (consistente con las aplicaciones y las bases). Los identificadores que son API de terceros se dejan como están.
|
|
22
|
+
- **Compilación sin fallbacks**: `npm run build` y `npm run typecheck` no toleran errores; no agregar `|| echo ...` a los scripts. Un error de tipos se corrige en el código.
|
|
23
|
+
- `npm install` reordena y reformatea todo `package.json`. Después de instalar algo, restaurar el archivo y agregar a mano solo la línea nueva (el `package-lock.json` sí lo maneja npm).
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Estructura
|
|
28
|
+
|
|
29
|
+
- `src/index.ts` — lo que exporta la librería. Lo que no está acá es interno.
|
|
30
|
+
- `src/App.tsx` — `App` y `FrontendPlusReactRoutes` (rutas, proveedores).
|
|
31
|
+
- `src/components/grid/` — la grilla:
|
|
32
|
+
- `GenericDataGrid.tsx`: orquesta todo (carga, columnas, detalles anidados, diálogos).
|
|
33
|
+
- `estadoGrilla.ts`: store por grilla para el feedback de guardado y las celdas pendientes (`useSyncExternalStore`).
|
|
34
|
+
- `detalles.tsx`: tipos y contextos de los detalles desplegados dentro de la grilla.
|
|
35
|
+
- `InputRenderer.tsx`: editor de celda y guardado de la fila.
|
|
36
|
+
- `renderers/`: celdas, encabezados, filas de resumen y editores.
|
|
37
|
+
- `src/hooks/grid/` — `useGridDataView` (filtros, orden, totales), `useGridColumns`, `useGridEvents` (teclado y edición), `useGridLayout` (alturas), `useGridActions`, `useGridMenu`.
|
|
38
|
+
- `src/utils/fetchApi.ts` — comunicación con el backend (codificación de parámetros).
|
|
39
|
+
- `src/store/` — Redux: contexto del cliente (menú, procedimientos), ruta y menú.
|
|
40
|
+
- `src/contexts/AppContext.tsx` — sesión (`keep-alive`) y cartel de sesión expirada.
|
|
41
|
+
- `src/components/` — `PrivateRoute`, `InitialRedirectHandler`, `MainLayout`, `ProcedureForm`, etc.
|
|
42
|
+
- `dist/` — salida de `tsup`. Está en `.gitignore`; no se edita ni se versiona.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Cosas que no son obvias (y se rompieron antes)
|
|
47
|
+
|
|
48
|
+
### Protocolo con el backend
|
|
49
|
+
- Cada parámetro viaja codificado según su `encoding` en `client-setup` (`JSON4all` por omisión, `plain`, `JSON`), igual que `my-ajax.js`; el servidor decodifica con el mismo encoding. Mandar `JSON.stringify` o texto crudo rompe fechas (llegan como string) y convierte textos como `"5"` en números. Los procedimientos `method:'get'` van por la URL.
|
|
50
|
+
- El resultado puede traer líneas de progreso antes de `--`; `parseBackendResponse` se queda con la última parte.
|
|
51
|
+
|
|
52
|
+
### Rendimiento de la grilla
|
|
53
|
+
- Las **columnas** (`useGridColumns`) no pueden depender de datos, feedback, orden ni filtros: si cambian en cada edición, react-data-grid redibuja todas las celdas. Lo que cambia por celda va al `EstadoGrilla`; la celda (`CeldaDato`) es un componente memorizado que se suscribe solo a lo suyo.
|
|
54
|
+
- No copiar las filas en cada render (`rows.map(...)`): anula la memorización de filas. El estilo por fila va por `rowClass`.
|
|
55
|
+
- No reemplazar la grilla por un spinner mientras hay una llamada en curso; el spinner es solo para la carga inicial.
|
|
56
|
+
- `useGridDataView` deja en su lugar la fila editada cuando cambian pocas filas (no reordena ni vuelve a filtrar en medio de una edición) y recalcula todo cuando cambia mucho (refrescar, importar).
|
|
57
|
+
- Las grillas se cargan con una sola petición por tabla y filtros aunque React monte los efectos dos veces (`cargasEnCurso`).
|
|
58
|
+
|
|
59
|
+
### Edición y teclado
|
|
60
|
+
- Al editar con un filtro activo, react-data-grid entrega solo las filas visibles: `handleRowsChange` reemplaza las filas editadas dentro de `tableData`, nunca lo pisa con lo recibido.
|
|
61
|
+
- **No esperar la respuesta del servidor para mover el foco** (Enter, Tab, flechas, F4): con la tecla apretada llegan más pulsaciones que respuestas. El cambio de la fila se aplica en el acto y el guardado sigue en segundo plano.
|
|
62
|
+
- **No llamar `onRowChange(fila, true)` del editor después de un `await`**: cierra el editor de la celda que esté seleccionada en ese momento (la siguiente). La fila que devuelve el servidor se aplica sobre los datos (`setTableData`).
|
|
63
|
+
- F4 con la tecla apretada: las pulsaciones que caen en la celda antes de que el editor nuevo tenga el foco se le pasan por `registrarEditor`, o quedan en cola hasta que haya uno. F4 usa el valor que acaba de copiar si la fila de arriba todavía no se redibujó.
|
|
64
|
+
|
|
65
|
+
### Detalles desplegados (como `backend-plus`)
|
|
66
|
+
- Un detalle es una fila especial (`FilaDetalle`, marcada con un `Symbol`) que se intercala debajo de su fila y ocupa todas las columnas; adentro hay otra `GenericDataGrid` (recursiva). Ctrl+click o botón del medio lo abre en otra pestaña.
|
|
67
|
+
- Alturas: **de arriba hacia abajo**. La grilla principal toma lo que queda de la ventana; cada detalle recibe `altoDisponible` y no puede pasar de ahí, así su pie queda visible. No calcular el tope con el alto medido de la grilla padre: una grilla de pocas filas es baja y aplasta a su detalle.
|
|
68
|
+
- Un detalle con muchas filas muestra `firstDisplayCount` (20) cuando supera `firstDisplayOverLimit` (30), los mismos umbrales de `backend-plus`, y **virtualiza** sus filas; los chicos crecen según su contenido. La grilla principal siempre virtualiza.
|
|
69
|
+
- Anchos: una grilla anidada es tan ancha como sus columnas; si un detalle abierto es más ancho, informa su ancho hacia arriba y el padre se ensancha.
|
|
70
|
+
- Un cache compartido por la jerarquía (`CacheGrillasContext`) permite volver a mostrar un detalle que salió de pantalla por la virtualización sin pedirlo otra vez.
|
|
71
|
+
- El título de una grilla anidada es el de su tabla, igual que el de la principal.
|
|
72
|
+
- **Detalle `wScreen`** (`{wScreen:'ticket', fields:[...]}`, como las anotaciones de un ticket en gestik): se dibuja la pantalla registrada en `myWScreens` y recibe `params` (los fixedFields del detalle como objeto plano, igual que `mainAction(params, div)` de backend-plus). La fila toma el alto de lo que la pantalla dibuja, hasta el máximo disponible, y scrollea por dentro. Las grillas que la pantalla arma (`<GenericDataGrid anidada .../>`) reciben ese máximo por `AltoDisponibleContext`. Con Ctrl+click o el botón del medio se abre en otra pestaña, en `/wScreens/<nombre>?campo=valor` (`PantallaDeRuta` pasa la query string como `params`).
|
|
73
|
+
- **`condition`** de un detalle: el nombre de una función registrada en `myConditions` (o `extendConditions`) que recibe la fila y dice si se muestra el botón; sin ella o si da falso, la celda queda vacía. Si el nombre no está registrado se avisa una vez por consola y se muestra el detalle.
|
|
74
|
+
- Lo que aporta la aplicación (`myWScreens`, `myClientSides`, `myResultsOk`, `myConditions`) se registra **antes del primer render**, no en un efecto: las rutas de las wScreens se arman al renderizar a partir del registro.
|
|
75
|
+
- El `ff` de un ítem de menú puede ser un arreglo `[{fieldName, value}]` (lo que documenta backend-plus) o un objeto `{campo: valor}` (lo que usa dmencu); `fixedFieldsDeMenu` entiende los dos.
|
|
76
|
+
|
|
77
|
+
### Sesión y arranque
|
|
78
|
+
- Con el estado todavía sin cargar (`idle`), `PrivateRoute` manda a `/login` guardando la ruta pedida; `InitialRedirectHandler` (montado fuera de las rutas privadas) vuelve a esa ruta cuando la sesión resulta válida. Tiene una guarda para no caer en `/home` mientras la ruta guardada se limpia.
|
|
79
|
+
- El cartel «¡Sesión expirada!» solo corresponde si hubo una sesión (guardada de la visita anterior o activa en esta carga). En la primera visita sin sesión, ir al login no lleva cartel.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Puente con el cliente de backend-plus (opcional, `src/puente/`)
|
|
84
|
+
|
|
85
|
+
Para no perder lo que la grilla de `backend-plus` ya hace y que acá todavía no está: la aplicación pasa `puenteBP` a `App` y las wScreens que **no** registró en `myWScreens` se piden al cliente de `backend-plus` (`myOwn.wScreens[nombre]`), en lugar de mostrar «no implementada». `GrillaBP` pone `myOwn.tableGrid` dentro de una página React; `/bp/table/:tableName` sirve para comparar las dos grillas. Lo propio de React siempre tiene prioridad.
|
|
86
|
+
|
|
87
|
+
- **Mismo origen que el backend, obligatoriamente.** Los scripts de `backend-plus` solo se sirven con sesión (sin ella redirigen al login) y su cliente es un singleton global (`myOwn`, `my`). La aplicación se sirve bajo `<base-url>/react/` o, en desarrollo, Vite hace de proxy (ver `mi-frontend`: `VITE_BASE=/ggs2026/react/ VITE_BACKEND_URL=/ggs2026 PROXY_BACKEND=http://localhost:3044`).
|
|
88
|
+
- `cargarClienteBP` lee la página `<base-url>/menu`, saca de ahí los `<script>` y `<link>` (los arma el servidor con `clientIncludes`, en orden) y los carga. Es un atajo: lo sano sería que el servidor exponga esa lista, y eso es cambiar `backend-plus` (consultar antes).
|
|
89
|
+
- 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ó).
|
|
90
|
+
- 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.
|
|
91
|
+
- **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`.
|
|
92
|
+
- **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. 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.
|
|
93
|
+
- 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.
|
|
94
|
+
- `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`).
|
|
95
|
+
- **No hay un entorno de prueba automático para esto**: jsdom no carga los scripts de `backend-plus`. Los tests cubren la extracción de recursos y la elección de pantalla; el resto se prueba en el navegador contra un backend real (headless: Chrome con `--screenshot` y un proxy chico que inyecte la cookie de sesión).
|
|
96
|
+
- Probado contra ggs (con Chrome controlado por el protocolo de depuración, sin escribir en la base): grilla `usuarios`, la wScreen `ayuda` y `abrir_encuesta` hasta ver el formulario de la encuesta de dmencu dentro de `mi-frontend`. Sin probar: edición, detalles anidados, exportar y la convivencia del CSS global de `backend-plus` con MUI.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Compilación y fuentes en F12
|
|
101
|
+
|
|
102
|
+
- `npm run build` (tsup, config en `tsup.config.ts`) genera `dist/` en ESM y CJS con `.d.ts`. **Sin `--splitting`**: con una sola entrada el `.map` de CJS sale roto. Los `.map` llevan el código fuente adentro (`sourcesContent`): en F12 se ven los `.ts`/`.tsx` originales.
|
|
103
|
+
- Las dependencias y `peerDependencies` quedan externas; las resuelve el bundler de la aplicación.
|
|
104
|
+
- `mi-frontend` tiene un plugin de Vite que encadena el `.map` de esta librería en el build de la aplicación (Rollup no lo hace solo).
|
|
105
|
+
- ⚠️ `npm install` dentro de una aplicación enlazada (`mi-frontend`) **reemplaza el `npm link`** y pisa `dist/` y `package.json` de esta carpeta con lo publicado. Después hay que rehacer el enlace (en Windows, `mklink /J`) y volver a compilar.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Tests
|
|
110
|
+
|
|
111
|
+
- **Framework**: Vitest + jsdom + Testing Library. **Comandos**: `npm test` (una pasada), `npx vitest` (modo vigilancia), `npm run typecheck`.
|
|
112
|
+
- Los tests viven al lado de lo que prueban (`*.test.ts` / `*.test.tsx`) y están en castellano. La preparación común está en `src/test/preparacion.ts`.
|
|
113
|
+
- Se cubren las funcionalidades críticas y, sobre todo, **cada bug que se arregla lleva su test**: se verifica que falle si se reintroduce el bug. Hoy (74 casos): codificación de parámetros, fixedFields de los detalles, detalle wScreen y `condition`, `PantallaDeRuta`, `ff` del menú, el store de la grilla, la vista de datos (orden, filtros, edición en el lugar), la edición con filtro, la cola de F4, la vuelta a la ruta pedida tras el login, el cartel de sesión expirada y los helpers.
|
|
114
|
+
- jsdom no calcula medidas ni trae `ResizeObserver`: lo que depende del layout (alturas, anchos, virtualización) se prueba en el navegador con `mi-frontend`, no en Vitest.
|
|
115
|
+
- Para el mock de `fetch` y de `fetchApi` usar `vi.stubGlobal` / `vi.mock`; no levantar servidores en los tests.
|
|
116
|
+
- Antes de commitear: `npm run typecheck`, `npm test` y `npm run build`.
|
|
117
|
+
|
|
118
|
+
### Pruebas en el navegador con un backend real
|
|
119
|
+
- `mi-frontend` sirve para probar con una aplicación real: `VITE_BACKEND_URL=/dmencu PROXY_BACKEND=http://localhost:3050 npx vite`. Con una ruta en `VITE_BACKEND_URL`, Vite hace de proxy y el navegador habla con un solo origen (el backend solo habilita CORS si tiene `server.allowedHosts`). En Git Bash agregar `MSYS_NO_PATHCONV=1` o la ruta se convierte en una ruta de Windows.
|
|
120
|
+
- Las credenciales de prueba se las da el usuario en el chat; **no escribirlas en archivos ni en la documentación**. Las pruebas contra una base real son de solo lectura salvo que el usuario diga otra cosa, y lo que se escriba se deja como estaba.
|
|
121
|
+
- Para medir rendimiento usar el build de producción: en desarrollo React valida de más y los tiempos engañan.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Publicación en npm
|
|
126
|
+
|
|
127
|
+
Es automática, igual que `dmencu`: `.github/workflows/publish.yml` llama al workflow reutilizable `codenautas/.github@v3` (`npm ci`, `npm run build`, tests y `npm publish`). No hay token: npm confía en el repo (Trusted Publisher configurado en npmjs.com para `codenautas/frontend-plus-react`, workflow `publish.yml`, sin environment).
|
|
128
|
+
|
|
129
|
+
Para publicar una versión (solo cuando el usuario lo pide):
|
|
130
|
+
1. Subir la versión: `npm version X.Y.Z --no-git-tag-version` (toca `package.json` y `package-lock.json`; npm no deja republicar un número ya publicado).
|
|
131
|
+
2. Commit y `git push origin main`.
|
|
132
|
+
3. Crear y subir el tag: `git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z`. El tag tiene que coincidir con la versión de `package.json` o el workflow falla.
|
|
133
|
+
4. Verificar en la pestaña Actions de GitHub y con `npm view frontend-plus-react version`. En esta máquina no está `gh`.
|
|
134
|
+
|
|
135
|
+
- Alternativa: correr el workflow a mano desde Actions en `main` (crea él mismo el tag `v<versión>`).
|
|
136
|
+
- Las versiones con `beta`, `alpha` o `rc` en el número se publican con ese dist-tag, no como `latest`.
|
|
137
|
+
- `package.json` necesita el campo `repository`; sin él npm no valida la publicación.
|
|
138
|
+
- `npm ci` exige el lock sincronizado: probar con `npm ci --dry-run` antes de taguear.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Cuándo parar y preguntar
|
|
143
|
+
|
|
144
|
+
- Si hace falta cambiar `backend-plus` para que algo ande: parar. Casi siempre hay un punto de extensión (o el cambio va en el protocolo del cliente).
|
|
145
|
+
- Si una propiedad de `TableDefinition`, `FieldDefinition` o `ProcedureDef` no aparece en `backend-plus.d.ts`: no inventarla.
|
|
146
|
+
- Si el comportamiento observado difiere de `backend-plus`: avisar de la diferencia en lugar de resolverla en silencio.
|
|
147
|
+
- No publicar en npm ni subir tags sin que el usuario lo pida.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Documentación
|
|
152
|
+
|
|
153
|
+
Mantener actualizados este archivo y el `README.md` cuando cambie el comportamiento de la grilla, el protocolo o el arranque. Las decisiones que se aprenden a los golpes (como las de arriba) van en la sección «Cosas que no son obvias», con el porqué.
|
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,7 @@ __export(src_exports, {
|
|
|
37
37
|
FrontendPlusProviders: () => FrontendPlusProviders,
|
|
38
38
|
FrontendPlusReactRoutes: () => FrontendPlusReactRoutes,
|
|
39
39
|
GenericDataGrid: () => GenericDataGrid_default,
|
|
40
|
+
GrillaBP: () => GrillaBP,
|
|
40
41
|
MainLayout: () => MainLayout_default,
|
|
41
42
|
SnackbarProvider: () => SnackbarProvider,
|
|
42
43
|
cambiarGuionesBajosPorEspacios: () => cambiarGuionesBajosPorEspacios,
|
|
@@ -46,6 +47,7 @@ __export(src_exports, {
|
|
|
46
47
|
extendResultsOk: () => extendResultsOk,
|
|
47
48
|
extendWScreens: () => extendWScreens,
|
|
48
49
|
extractPathsFromRoutes: () => extractPathsFromRoutes,
|
|
50
|
+
habilitarPuenteBP: () => habilitarPuenteBP,
|
|
49
51
|
resultComponents: () => resultsOk,
|
|
50
52
|
useApiCall: () => useApiCall,
|
|
51
53
|
useApiCallWithoutSnackbar: () => useApiCallWithoutSnackbar,
|
|
@@ -3451,6 +3453,11 @@ var VerticalEditorPage = ({
|
|
|
3451
3453
|
] });
|
|
3452
3454
|
};
|
|
3453
3455
|
|
|
3456
|
+
// src/rutaBase.ts
|
|
3457
|
+
var import_meta2 = {};
|
|
3458
|
+
var baseDelBundler = import_meta2.env?.BASE_URL ?? "/";
|
|
3459
|
+
var rutaBase = baseDelBundler.replace(/\/+$/, "");
|
|
3460
|
+
|
|
3454
3461
|
// src/components/grid/GenericDataGrid.tsx
|
|
3455
3462
|
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
3456
3463
|
var SUMMARY_ROW_HEIGHT = 35;
|
|
@@ -3840,7 +3847,7 @@ var GenericDataGrid = ({
|
|
|
3840
3847
|
const abrirWScreenEnPestana = (0, import_react22.useCallback)((row, detailTable) => {
|
|
3841
3848
|
const parametros = new URLSearchParams();
|
|
3842
3849
|
calcularFixedFieldsDetalle(detailTable, row).forEach((ff) => parametros.set(ff.fieldName, String(ff.value)));
|
|
3843
|
-
window.open(`${window.location.origin}/wScreens/${detailTable.wScreen}?${parametros}`, "_blank");
|
|
3850
|
+
window.open(`${window.location.origin}${rutaBase}/wScreens/${detailTable.wScreen}?${parametros}`, "_blank");
|
|
3844
3851
|
}, []);
|
|
3845
3852
|
const alternarDetalle = (0, import_react22.useCallback)((row, rowId, detailTable, enOtraPestana) => {
|
|
3846
3853
|
if (enOtraPestana && detailTable.wScreen) {
|
|
@@ -4183,8 +4190,8 @@ var GenericDataGrid = ({
|
|
|
4183
4190
|
var GenericDataGrid_default = GenericDataGrid;
|
|
4184
4191
|
|
|
4185
4192
|
// src/App.tsx
|
|
4186
|
-
var
|
|
4187
|
-
var
|
|
4193
|
+
var import_react37 = __toESM(require("react"), 1);
|
|
4194
|
+
var import_react_router_dom14 = require("react-router-dom");
|
|
4188
4195
|
|
|
4189
4196
|
// src/components/MainLayout.tsx
|
|
4190
4197
|
var import_react26 = require("react");
|
|
@@ -5368,7 +5375,7 @@ var ProcedureForm_default = ProcedureForm;
|
|
|
5368
5375
|
|
|
5369
5376
|
// src/App.tsx
|
|
5370
5377
|
var import_react_redux5 = require("react-redux");
|
|
5371
|
-
var
|
|
5378
|
+
var import_react38 = require("redux-persist/integration/react");
|
|
5372
5379
|
|
|
5373
5380
|
// src/components/PrivateRoute.tsx
|
|
5374
5381
|
var import_react30 = require("react");
|
|
@@ -5599,62 +5606,330 @@ var MenuTablePage = () => {
|
|
|
5599
5606
|
};
|
|
5600
5607
|
var MenuTablePage_default = MenuTablePage;
|
|
5601
5608
|
|
|
5602
|
-
// src/
|
|
5609
|
+
// src/puente/PantallaBP.tsx
|
|
5610
|
+
var import_react35 = require("react");
|
|
5611
|
+
var import_react_router_dom12 = require("react-router-dom");
|
|
5603
5612
|
var import_material27 = require("@mui/material");
|
|
5604
|
-
|
|
5613
|
+
|
|
5614
|
+
// src/puente/clienteBP.ts
|
|
5615
|
+
function extraerRecursos(html) {
|
|
5616
|
+
const documento = new DOMParser().parseFromString(html, "text/html");
|
|
5617
|
+
const referencias = (selector, atributo) => Array.from(documento.querySelectorAll(selector)).map((elemento) => elemento.getAttribute(atributo)).filter((ruta) => !!ruta);
|
|
5618
|
+
return {
|
|
5619
|
+
hojasDeEstilo: referencias("link[rel=stylesheet]", "href"),
|
|
5620
|
+
scripts: referencias("script[src]", "src")
|
|
5621
|
+
};
|
|
5622
|
+
}
|
|
5623
|
+
function fijarBase(urlBackend) {
|
|
5624
|
+
const href = new URL(urlBackend.replace(/\/*$/, "/"), window.location.href).href;
|
|
5625
|
+
let base = document.querySelector("base");
|
|
5626
|
+
if (!base) {
|
|
5627
|
+
base = document.createElement("base");
|
|
5628
|
+
document.head.prepend(base);
|
|
5629
|
+
}
|
|
5630
|
+
base.href = href;
|
|
5631
|
+
}
|
|
5632
|
+
function cargarScript(ruta) {
|
|
5633
|
+
return new Promise((resolver, rechazar) => {
|
|
5634
|
+
const script = document.createElement("script");
|
|
5635
|
+
script.async = false;
|
|
5636
|
+
script.src = ruta;
|
|
5637
|
+
script.onload = () => resolver();
|
|
5638
|
+
script.onerror = () => rechazar(new Error(`No se pudo cargar ${ruta}`));
|
|
5639
|
+
document.head.appendChild(script);
|
|
5640
|
+
});
|
|
5641
|
+
}
|
|
5642
|
+
var carga = null;
|
|
5643
|
+
function cargarClienteBP(urlBackend) {
|
|
5644
|
+
if (!carga) {
|
|
5645
|
+
carga = cargarSinMemoria(urlBackend).catch((error) => {
|
|
5646
|
+
carga = null;
|
|
5647
|
+
throw error;
|
|
5648
|
+
});
|
|
5649
|
+
}
|
|
5650
|
+
return carga;
|
|
5651
|
+
}
|
|
5652
|
+
async function cargarSinMemoria(urlBackend) {
|
|
5653
|
+
const destino = new URL(urlBackend, window.location.href);
|
|
5654
|
+
if (destino.origin !== window.location.origin) {
|
|
5655
|
+
throw new Error(`El cliente de backend-plus solo se puede usar desde el mismo origen que el backend (${destino.origin} no es ${window.location.origin}).`);
|
|
5656
|
+
}
|
|
5657
|
+
fijarBase(urlBackend);
|
|
5658
|
+
const respuesta = await fetch(`${destino.pathname.replace(/\/*$/, "")}/menu`, { credentials: "include" });
|
|
5659
|
+
if (!respuesta.ok || /\/login\/?$/.test(new URL(respuesta.url).pathname)) {
|
|
5660
|
+
throw new Error("No hay una sesi\xF3n iniciada en el backend.");
|
|
5661
|
+
}
|
|
5662
|
+
const recursos = extraerRecursos(await respuesta.text());
|
|
5663
|
+
recursos.hojasDeEstilo.forEach((ruta) => {
|
|
5664
|
+
const enlace = document.createElement("link");
|
|
5665
|
+
enlace.rel = "stylesheet";
|
|
5666
|
+
enlace.href = ruta;
|
|
5667
|
+
document.head.appendChild(enlace);
|
|
5668
|
+
});
|
|
5669
|
+
for (const ruta of recursos.scripts) {
|
|
5670
|
+
await cargarScript(ruta);
|
|
5671
|
+
}
|
|
5672
|
+
const myOwn = window.myOwn;
|
|
5673
|
+
if (!myOwn) throw new Error("El backend no defini\xF3 myOwn.");
|
|
5674
|
+
window.my = myOwn;
|
|
5675
|
+
await myOwn.autoSetup();
|
|
5676
|
+
return myOwn;
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
// src/puente/pantallaDeBP.ts
|
|
5680
|
+
var RUTA_PANTALLA_BP = "/bp/menu";
|
|
5681
|
+
function rutaDePantallaBP(nombre, parametros = "") {
|
|
5682
|
+
const resto = parametros.replace(/^[#?]/, "").replace(/(^|&)w=[^&]*/, "").replace(/^&/, "");
|
|
5683
|
+
return `${RUTA_PANTALLA_BP}#w=${encodeURIComponent(nombre)}${resto ? `&${resto}` : ""}`;
|
|
5684
|
+
}
|
|
5685
|
+
function rutaDeEnlaceBP(href, baseDelDocumento, urlBackend) {
|
|
5686
|
+
const enlace = new URL(href, baseDelDocumento);
|
|
5687
|
+
const base = `${urlBackend.replace(/\/*$/, "/")}`;
|
|
5688
|
+
const pagina = (nombre2) => new URL(`${base}${nombre2}`, baseDelDocumento);
|
|
5689
|
+
if (enlace.origin !== pagina("menu").origin) return null;
|
|
5690
|
+
if (enlace.pathname === pagina("login").pathname) return "/login";
|
|
5691
|
+
if (enlace.pathname === pagina("logout").pathname) return "/logout";
|
|
5692
|
+
if (enlace.pathname !== pagina("menu").pathname) return null;
|
|
5693
|
+
const texto = (enlace.hash || enlace.search).slice(1);
|
|
5694
|
+
const nombre = new URLSearchParams(texto).get("w");
|
|
5695
|
+
return nombre ? rutaDePantallaBP(nombre, texto) : null;
|
|
5696
|
+
}
|
|
5697
|
+
function resolverPantallaBP(myOwn, nombre, contenedor) {
|
|
5698
|
+
const registrada = myOwn.wScreens[nombre];
|
|
5699
|
+
if (!registrada) return null;
|
|
5700
|
+
if (typeof registrada === "function") {
|
|
5701
|
+
return { definicion: { mainFunction: registrada }, ejecutar: registrada };
|
|
5702
|
+
}
|
|
5703
|
+
if (registrada.mainFunction) {
|
|
5704
|
+
return { definicion: registrada, ejecutar: registrada.mainFunction };
|
|
5705
|
+
}
|
|
5706
|
+
const { parameters, mainAction, resultClass, proceedLabel } = registrada;
|
|
5707
|
+
const procAux = myOwn.wScreens.procAux;
|
|
5708
|
+
const mostrarParametros = typeof procAux === "object" ? procAux.showParams : void 0;
|
|
5709
|
+
if (parameters && mostrarParametros) {
|
|
5710
|
+
return {
|
|
5711
|
+
definicion: registrada,
|
|
5712
|
+
ejecutar: (parametros) => mostrarParametros({ parameters, resultClass, proceedLabel }, contenedor, parametros, mainAction)
|
|
5713
|
+
};
|
|
5714
|
+
}
|
|
5715
|
+
return null;
|
|
5716
|
+
}
|
|
5717
|
+
|
|
5718
|
+
// src/puente/capturaDeNavegacion.ts
|
|
5719
|
+
function apiDeNavegacion() {
|
|
5720
|
+
const api = Reflect.get(window, "navigation");
|
|
5721
|
+
return typeof api === "object" && api !== null && "addEventListener" in api && typeof api.addEventListener === "function" ? api : null;
|
|
5722
|
+
}
|
|
5723
|
+
function capturarNavegacionBP(urlBackend, navegar) {
|
|
5724
|
+
const alHacerClick = (evento) => {
|
|
5725
|
+
if (evento.defaultPrevented || evento.button !== 0 || evento.ctrlKey || evento.metaKey || evento.shiftKey) return;
|
|
5726
|
+
const enlace = evento.target instanceof Element ? evento.target.closest("a[href]") : null;
|
|
5727
|
+
if (!enlace || enlace.hasAttribute("download") || (enlace.getAttribute("target") ?? "_self") !== "_self") return;
|
|
5728
|
+
const ruta = rutaDeEnlaceBP(enlace.getAttribute("href") ?? "", document.baseURI, urlBackend);
|
|
5729
|
+
if (ruta) {
|
|
5730
|
+
evento.preventDefault();
|
|
5731
|
+
evento.stopPropagation();
|
|
5732
|
+
navegar(ruta);
|
|
5733
|
+
}
|
|
5734
|
+
};
|
|
5735
|
+
document.addEventListener("click", alHacerClick, true);
|
|
5736
|
+
const navegacion = apiDeNavegacion();
|
|
5737
|
+
const alNavegar = (evento) => {
|
|
5738
|
+
if (!evento.cancelable || evento.destination.sameDocument || evento.downloadRequest !== null) return;
|
|
5739
|
+
const ruta = rutaDeEnlaceBP(evento.destination.url, document.baseURI, urlBackend);
|
|
5740
|
+
if (ruta) {
|
|
5741
|
+
evento.preventDefault();
|
|
5742
|
+
navegar(ruta);
|
|
5743
|
+
}
|
|
5744
|
+
};
|
|
5745
|
+
navegacion?.addEventListener("navigate", alNavegar);
|
|
5746
|
+
return () => {
|
|
5747
|
+
document.removeEventListener("click", alHacerClick, true);
|
|
5748
|
+
navegacion?.removeEventListener("navigate", alNavegar);
|
|
5749
|
+
};
|
|
5750
|
+
}
|
|
5751
|
+
|
|
5752
|
+
// src/puente/habilitacion.ts
|
|
5753
|
+
var habilitado = false;
|
|
5754
|
+
var habilitarPuenteBP = () => {
|
|
5755
|
+
habilitado = true;
|
|
5756
|
+
};
|
|
5757
|
+
var puenteBPHabilitado = () => habilitado;
|
|
5758
|
+
|
|
5759
|
+
// src/puente/PantallaBP.tsx
|
|
5605
5760
|
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
5761
|
+
function buscarItemDeMenu(items, tipo) {
|
|
5762
|
+
for (const item of items) {
|
|
5763
|
+
if (item.menuType === tipo) return item;
|
|
5764
|
+
if (item.menuType === "menu") {
|
|
5765
|
+
const encontrado = buscarItemDeMenu(item.menuContent, tipo);
|
|
5766
|
+
if (encontrado) return encontrado;
|
|
5767
|
+
}
|
|
5768
|
+
}
|
|
5769
|
+
return void 0;
|
|
5770
|
+
}
|
|
5771
|
+
var PantallaBP = ({ nombre, direccion }) => {
|
|
5772
|
+
const navigate = (0, import_react_router_dom12.useNavigate)();
|
|
5773
|
+
const { clientContext } = useApp();
|
|
5774
|
+
const [cargando, setCargando] = (0, import_react35.useState)(true);
|
|
5775
|
+
const [error, setError] = (0, import_react35.useState)(null);
|
|
5776
|
+
const [noExiste, setNoExiste] = (0, import_react35.useState)(false);
|
|
5777
|
+
const contenedor = (0, import_react35.useRef)(null);
|
|
5778
|
+
const menu = clientContext.menu;
|
|
5779
|
+
(0, import_react35.useEffect)(() => {
|
|
5780
|
+
const elemento = contenedor.current;
|
|
5781
|
+
if (!elemento) return;
|
|
5782
|
+
let vigente = true;
|
|
5783
|
+
setError(null);
|
|
5784
|
+
setNoExiste(false);
|
|
5785
|
+
setCargando(true);
|
|
5786
|
+
const dejarDeCapturar = capturarNavegacionBP(envConfig.backendUrl, navigate);
|
|
5787
|
+
cargarClienteBP(envConfig.backendUrl).then((myOwn) => {
|
|
5788
|
+
if (!vigente) return;
|
|
5789
|
+
setCargando(false);
|
|
5790
|
+
const pantalla = resolverPantallaBP(myOwn, nombre, elemento);
|
|
5791
|
+
if (!pantalla) {
|
|
5792
|
+
setNoExiste(true);
|
|
5793
|
+
return;
|
|
5794
|
+
}
|
|
5795
|
+
elemento.replaceChildren();
|
|
5796
|
+
const item = menu ? buscarItemDeMenu(menu, nombre) : void 0;
|
|
5797
|
+
const desdeLaDireccion = myOwn.UriSearchToObject?.(direccion) ?? Object.fromEntries(new URLSearchParams(direccion.replace(/^[#?]/, "")).entries());
|
|
5798
|
+
const parametros = { ...item, ...desdeLaDireccion };
|
|
5799
|
+
myOwn.preDisplayPage?.(parametros, pantalla.definicion, nombre);
|
|
5800
|
+
pantalla.ejecutar.call(myOwn, parametros);
|
|
5801
|
+
}).catch((err) => {
|
|
5802
|
+
if (!vigente) return;
|
|
5803
|
+
setCargando(false);
|
|
5804
|
+
setError(mensajeDeError(err));
|
|
5805
|
+
});
|
|
5806
|
+
return () => {
|
|
5807
|
+
vigente = false;
|
|
5808
|
+
dejarDeCapturar();
|
|
5809
|
+
elemento.replaceChildren();
|
|
5810
|
+
};
|
|
5811
|
+
}, [nombre, direccion, menu, navigate]);
|
|
5812
|
+
if (noExiste) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(FallbackWScreen, { screenName: nombre });
|
|
5813
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_material27.Box, { children: [
|
|
5814
|
+
cargando && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_material27.CircularProgress, { size: 24, sx: { m: 2 } }),
|
|
5815
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_material27.Alert, { severity: "error", sx: { m: 2 }, children: error }),
|
|
5816
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { id: "main_layout", ref: contenedor })
|
|
5817
|
+
] });
|
|
5818
|
+
};
|
|
5819
|
+
var PantallaDeRespaldo = () => {
|
|
5820
|
+
const { screenName = "" } = (0, import_react_router_dom12.useParams)();
|
|
5821
|
+
const { search } = (0, import_react_router_dom12.useLocation)();
|
|
5822
|
+
if (!puenteBPHabilitado()) {
|
|
5823
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_material27.Box, { sx: { p: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(FallbackWScreen, { screenName }) });
|
|
5824
|
+
}
|
|
5825
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_router_dom12.Navigate, { to: rutaDePantallaBP(screenName, search), replace: true });
|
|
5826
|
+
};
|
|
5827
|
+
var PaginaBP = () => {
|
|
5828
|
+
const { hash, search } = (0, import_react_router_dom12.useLocation)();
|
|
5829
|
+
const direccion = hash || search;
|
|
5830
|
+
const nombre = new URLSearchParams(direccion.replace(/^[#?]/, "")).get("w");
|
|
5831
|
+
if (!puenteBPHabilitado()) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_router_dom12.Navigate, { to: "/", replace: true });
|
|
5832
|
+
if (!nombre) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_material27.Box, { sx: { p: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_material27.Alert, { severity: "warning", children: [
|
|
5833
|
+
"Falta la pantalla de backend-plus (",
|
|
5834
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("code", { children: "#w=..." }),
|
|
5835
|
+
")."
|
|
5836
|
+
] }) });
|
|
5837
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(PantallaBP, { nombre, direccion }, nombre);
|
|
5838
|
+
};
|
|
5839
|
+
|
|
5840
|
+
// src/puente/GrillaBP.tsx
|
|
5841
|
+
var import_react36 = require("react");
|
|
5842
|
+
var import_react_router_dom13 = require("react-router-dom");
|
|
5843
|
+
var import_material28 = require("@mui/material");
|
|
5844
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
5845
|
+
var GrillaBP = ({ tabla, fixedFields }) => {
|
|
5846
|
+
const contenedor = (0, import_react36.useRef)(null);
|
|
5847
|
+
const [cargando, setCargando] = (0, import_react36.useState)(true);
|
|
5848
|
+
const [error, setError] = (0, import_react36.useState)(null);
|
|
5849
|
+
const clave = JSON.stringify(fixedFields ?? []);
|
|
5850
|
+
(0, import_react36.useEffect)(() => {
|
|
5851
|
+
const elemento = contenedor.current;
|
|
5852
|
+
if (!elemento) return;
|
|
5853
|
+
let vigente = true;
|
|
5854
|
+
setError(null);
|
|
5855
|
+
setCargando(true);
|
|
5856
|
+
cargarClienteBP(envConfig.backendUrl).then((myOwn) => {
|
|
5857
|
+
if (!vigente) return;
|
|
5858
|
+
setCargando(false);
|
|
5859
|
+
elemento.replaceChildren();
|
|
5860
|
+
myOwn.tableGrid(tabla, elemento, { fixedFields: JSON.parse(clave) });
|
|
5861
|
+
}).catch((err) => {
|
|
5862
|
+
if (!vigente) return;
|
|
5863
|
+
setCargando(false);
|
|
5864
|
+
setError(mensajeDeError(err));
|
|
5865
|
+
});
|
|
5866
|
+
return () => {
|
|
5867
|
+
vigente = false;
|
|
5868
|
+
elemento.replaceChildren();
|
|
5869
|
+
};
|
|
5870
|
+
}, [tabla, clave]);
|
|
5871
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_material28.Box, { children: [
|
|
5872
|
+
cargando && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_material28.CircularProgress, { size: 24, sx: { m: 2 } }),
|
|
5873
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_material28.Alert, { severity: "error", sx: { m: 2 }, children: error }),
|
|
5874
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: contenedor })
|
|
5875
|
+
] });
|
|
5876
|
+
};
|
|
5877
|
+
var PaginaGrillaBP = () => {
|
|
5878
|
+
const { tableName = "" } = (0, import_react_router_dom13.useParams)();
|
|
5879
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(GrillaBP, { tabla: tableName }, tableName);
|
|
5880
|
+
};
|
|
5881
|
+
|
|
5882
|
+
// src/App.tsx
|
|
5883
|
+
var import_type_store6 = __toESM(require("type-store"), 1);
|
|
5884
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
5606
5885
|
var LocationTracker = () => {
|
|
5607
|
-
const location = (0,
|
|
5886
|
+
const location = (0, import_react_router_dom14.useLocation)();
|
|
5608
5887
|
const dispatch = useAppDispatch();
|
|
5609
|
-
(0,
|
|
5888
|
+
(0, import_react37.useEffect)(() => {
|
|
5610
5889
|
dispatch(setCurrentPath(location.pathname + location.search));
|
|
5611
5890
|
}, [location, dispatch]);
|
|
5612
5891
|
return null;
|
|
5613
5892
|
};
|
|
5614
5893
|
function FrontendPlusReactRoutes({ myRoutes, myUnloggedRoutes, layout: Layout = MainLayout_default, unloggedLayout: UnloggedLayout }) {
|
|
5615
|
-
return /* @__PURE__ */ (0,
|
|
5616
|
-
/* @__PURE__ */ (0,
|
|
5617
|
-
/* @__PURE__ */ (0,
|
|
5618
|
-
/* @__PURE__ */ (0,
|
|
5619
|
-
/* @__PURE__ */ (0,
|
|
5620
|
-
/* @__PURE__ */ (0,
|
|
5894
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
5895
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(InitialRedirectHandler_default, {}),
|
|
5896
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_router_dom14.Routes, { children: [
|
|
5897
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_router_dom14.Route, { element: UnloggedLayout ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(UnloggedLayout, {}) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Outlet, {}), children: [
|
|
5898
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "/login", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LoginPage_default, {}) }),
|
|
5899
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "/logout", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LogoutPage_default, {}) }),
|
|
5621
5900
|
myUnloggedRoutes
|
|
5622
5901
|
] }),
|
|
5623
|
-
/* @__PURE__ */ (0,
|
|
5624
|
-
/* @__PURE__ */ (0,
|
|
5625
|
-
/* @__PURE__ */ (0,
|
|
5626
|
-
/* @__PURE__ */ (0,
|
|
5627
|
-
/* @__PURE__ */ (0,
|
|
5628
|
-
/* @__PURE__ */ (0,
|
|
5629
|
-
Object.keys(wScreens).map((screenName) => /* @__PURE__ */ (0,
|
|
5630
|
-
|
|
5902
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_router_dom14.Route, { element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(PrivateRoute_default, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Layout, {}) }), children: [
|
|
5903
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "/", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(HomePage_default, {}) }),
|
|
5904
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "/home", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(HomePage_default, {}) }),
|
|
5905
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "/table/:tableName", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(GenericDataGridPage_default, {}) }),
|
|
5906
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "/menu/:menuName", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(MenuTablePage_default, {}) }),
|
|
5907
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "/procedures/:procedureName", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ProcedureForm_default, {}) }),
|
|
5908
|
+
Object.keys(wScreens).map((screenName) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
5909
|
+
import_react_router_dom14.Route,
|
|
5631
5910
|
{
|
|
5632
5911
|
path: `/wScreens/${screenName}`,
|
|
5633
|
-
element: /* @__PURE__ */ (0,
|
|
5912
|
+
element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(PantallaDeRuta, { screenName })
|
|
5634
5913
|
},
|
|
5635
5914
|
screenName
|
|
5636
5915
|
)),
|
|
5637
|
-
/* @__PURE__ */ (0,
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
path: "/wScreens-fallback/:screenName",
|
|
5641
|
-
element: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_material27.Box, { sx: { p: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(FallbackWScreen, { screenName: ":screenName" }) })
|
|
5642
|
-
}
|
|
5643
|
-
),
|
|
5916
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "/wScreens-fallback/:screenName", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(PantallaDeRespaldo, {}) }),
|
|
5917
|
+
puenteBPHabilitado() && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "/bp/menu", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(PaginaBP, {}) }),
|
|
5918
|
+
puenteBPHabilitado() && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "/bp/table/:tableName", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(PaginaGrillaBP, {}) }),
|
|
5644
5919
|
myRoutes,
|
|
5645
|
-
/* @__PURE__ */ (0,
|
|
5920
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { style: { marginTop: "20px", marginLeft: "10px" }, children: "404 - Recurso No Encontrado" }) })
|
|
5646
5921
|
] }),
|
|
5647
|
-
/* @__PURE__ */ (0,
|
|
5922
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_router_dom14.Navigate, { to: "/", replace: true }) })
|
|
5648
5923
|
] })
|
|
5649
5924
|
] });
|
|
5650
5925
|
}
|
|
5651
5926
|
var FrontendPlusProviders = ({ children, publicPaths }) => {
|
|
5652
|
-
return /* @__PURE__ */ (0,
|
|
5653
|
-
/* @__PURE__ */ (0,
|
|
5927
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_redux5.Provider, { store, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react38.PersistGate, { loading: null, persistor, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_router_dom14.BrowserRouter, { basename: rutaBase, children: [
|
|
5928
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LocationTracker, {}),
|
|
5654
5929
|
" ",
|
|
5655
|
-
/* @__PURE__ */ (0,
|
|
5930
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(AppProvider, { publicPaths, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(SnackbarProvider, { children: [
|
|
5656
5931
|
children,
|
|
5657
|
-
/* @__PURE__ */ (0,
|
|
5932
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(SessionExpiredMessage_default, {})
|
|
5658
5933
|
] }) })
|
|
5659
5934
|
] }) }) });
|
|
5660
5935
|
};
|
|
@@ -5665,9 +5940,10 @@ var App = ({
|
|
|
5665
5940
|
myWScreens,
|
|
5666
5941
|
myClientSides,
|
|
5667
5942
|
myResultsOk,
|
|
5668
|
-
myConditions
|
|
5943
|
+
myConditions,
|
|
5944
|
+
puenteBP
|
|
5669
5945
|
}) => {
|
|
5670
|
-
(0,
|
|
5946
|
+
(0, import_react37.useMemo)(() => {
|
|
5671
5947
|
if (myClientSides) {
|
|
5672
5948
|
extendClientSides(myClientSides);
|
|
5673
5949
|
}
|
|
@@ -5680,8 +5956,11 @@ var App = ({
|
|
|
5680
5956
|
if (myConditions) {
|
|
5681
5957
|
extendConditions(myConditions);
|
|
5682
5958
|
}
|
|
5683
|
-
|
|
5684
|
-
|
|
5959
|
+
if (puenteBP) {
|
|
5960
|
+
habilitarPuenteBP();
|
|
5961
|
+
}
|
|
5962
|
+
}, [myClientSides, myResultsOk, myWScreens, myConditions, puenteBP]);
|
|
5963
|
+
(0, import_react37.useEffect)(() => {
|
|
5685
5964
|
import_type_store6.default.messages = import_type_store6.default.i18n.messages.es;
|
|
5686
5965
|
import_type_store6.default.locale = import_type_store6.default.i18n.locale.es;
|
|
5687
5966
|
const link = document.querySelector("link[rel*='icon']") || document.createElement("link");
|
|
@@ -5690,11 +5969,11 @@ var App = ({
|
|
|
5690
5969
|
link.href = `${envConfig.backendUrl}/img/logo-128.png`;
|
|
5691
5970
|
document.getElementsByTagName("head")[0].appendChild(link);
|
|
5692
5971
|
}, []);
|
|
5693
|
-
const safePaths =
|
|
5972
|
+
const safePaths = import_react37.default.useMemo(() => {
|
|
5694
5973
|
const extracted = extractPathsFromRoutes(myUnloggedRoutes);
|
|
5695
5974
|
return ["/login", "/logout", ...extracted];
|
|
5696
5975
|
}, [myUnloggedRoutes]);
|
|
5697
|
-
return /* @__PURE__ */ (0,
|
|
5976
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(FrontendPlusProviders, { publicPaths: safePaths, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
5698
5977
|
FrontendPlusReactRoutes,
|
|
5699
5978
|
{
|
|
5700
5979
|
myRoutes,
|
|
@@ -5713,6 +5992,7 @@ var App_default = App;
|
|
|
5713
5992
|
FrontendPlusProviders,
|
|
5714
5993
|
FrontendPlusReactRoutes,
|
|
5715
5994
|
GenericDataGrid,
|
|
5995
|
+
GrillaBP,
|
|
5716
5996
|
MainLayout,
|
|
5717
5997
|
SnackbarProvider,
|
|
5718
5998
|
cambiarGuionesBajosPorEspacios,
|
|
@@ -5722,6 +6002,7 @@ var App_default = App;
|
|
|
5722
6002
|
extendResultsOk,
|
|
5723
6003
|
extendWScreens,
|
|
5724
6004
|
extractPathsFromRoutes,
|
|
6005
|
+
habilitarPuenteBP,
|
|
5725
6006
|
resultComponents,
|
|
5726
6007
|
useApiCall,
|
|
5727
6008
|
useApiCallWithoutSnackbar,
|