dmencu 2.2.16 → 3.0.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.
Files changed (47) hide show
  1. package/dist/client/client/client.d.ts +4 -1
  2. package/dist/client/client/client.js +165 -271
  3. package/dist/client/unlogged/abrir-formulario.d.ts +1 -8
  4. package/dist/client/unlogged/abrir-formulario.js +7 -48
  5. package/dist/client/unlogged/bypass-formulario.d.ts +2 -2
  6. package/dist/client/unlogged/bypass-formulario.js +26 -14
  7. package/dist/client/unlogged/redux-formulario.d.ts +141 -92
  8. package/dist/client/unlogged/redux-formulario.js +36 -85
  9. package/dist/client/unlogged/render-config.d.ts +52 -0
  10. package/dist/client/unlogged/render-config.js +161 -0
  11. package/dist/client/unlogged/render-formulario.d.ts +139 -0
  12. package/dist/client/unlogged/render-formulario.js +2045 -0
  13. package/dist/client/unlogged/render-formulario.tsx +610 -234
  14. package/dist/client/unlogged/render-general.d.ts +147 -0
  15. package/dist/client/unlogged/render-general.js +254 -0
  16. package/dist/client/unlogged/render-general.tsx +9 -3
  17. package/dist/client/unlogged/render-init.d.ts +17 -0
  18. package/dist/client/unlogged/render-init.js +127 -0
  19. package/dist/client/unlogged/tipos.d.ts +6 -4
  20. package/dist/client/unlogged/tipos.js +1 -1
  21. package/dist/server/server/app-dmencu.d.ts +8 -2
  22. package/dist/server/server/app-dmencu.js +20 -9
  23. package/dist/server/server/procedures-dmencu.d.ts +1 -1
  24. package/dist/server/server/procedures-dmencu.js +24 -21
  25. package/dist/server/server/table-sincronizaciones.js +3 -2
  26. package/dist/server/unlogged/tipos.d.ts +6 -4
  27. package/dist/server/unlogged/tipos.js +1 -1
  28. package/dist/unlogged/unlogged/abrir-formulario.d.ts +1 -8
  29. package/dist/unlogged/unlogged/abrir-formulario.js +7 -48
  30. package/dist/unlogged/unlogged/bypass-formulario.d.ts +2 -2
  31. package/dist/unlogged/unlogged/bypass-formulario.js +26 -14
  32. package/dist/unlogged/unlogged/redux-formulario.d.ts +141 -92
  33. package/dist/unlogged/unlogged/redux-formulario.js +36 -85
  34. package/dist/unlogged/unlogged/render-config.d.ts +52 -0
  35. package/dist/unlogged/unlogged/render-config.js +161 -0
  36. package/dist/unlogged/unlogged/render-formulario.d.ts +57 -10
  37. package/dist/unlogged/unlogged/render-formulario.js +365 -171
  38. package/dist/unlogged/unlogged/render-formulario.tsx +610 -234
  39. package/dist/unlogged/unlogged/render-general.d.ts +10 -0
  40. package/dist/unlogged/unlogged/render-general.js +8 -4
  41. package/dist/unlogged/unlogged/render-general.tsx +9 -3
  42. package/dist/unlogged/unlogged/render-init.d.ts +17 -0
  43. package/dist/unlogged/unlogged/render-init.js +127 -0
  44. package/dist/unlogged/unlogged/tipos.d.ts +6 -4
  45. package/dist/unlogged/unlogged/tipos.js +1 -1
  46. package/dist/unlogged/unlogged/unlogged.js +234 -50
  47. package/package.json +1 -1
@@ -7,7 +7,7 @@ import {
7
7
  scrollToTop,
8
8
  materialIoIconsSvgPath,
9
9
  useOnlineStatus,
10
- RenderizadorJSON
10
+ RenderizadorJSON,
11
11
  } from "./render-general";
12
12
  import {
13
13
  Bloque, BotonFormulario,
@@ -24,7 +24,7 @@ import {
24
24
  Texto, Estructura, InformacionHdr, ConfiguracionSorteoFormulario, ResumenEstado, IdOperativo, IdEnc, Libre,
25
25
  iterator, empty, ConfiguracionHabilitarBotonFormulario,
26
26
  PMatriz,
27
- ModoDM
27
+ ModoDM,
28
28
  } from "./tipos";
29
29
  import {
30
30
  accion_abrir_formulario,
@@ -34,16 +34,18 @@ import {
34
34
  calcularPermiteBorrarBF,
35
35
  calcularResumenVivienda,
36
36
  crearEncuesta,
37
+ hayDatosByPassAlmacenados,
37
38
  getDatosByPass,
38
39
  getFormulariosForValuePkRaiz,
39
40
  getMainFormForVivienda,
40
41
  intentarBackup,
41
42
  respuestasForPk,
42
- setCalcularVariables
43
+ setCalcularVariables,
44
+ setDatosByPass,
45
+ setEstructura
43
46
  } from "./bypass-formulario"
44
47
  import {
45
- dmTraerDatosFormulario, dispatchers,
46
- gotoSincronizar,
48
+ crearStoreFormulario, dispatchers,
47
49
  gotoConsistir,
48
50
  } from "./redux-formulario";
49
51
  import { useState, useEffect } from "react";
@@ -56,6 +58,9 @@ import {
56
58
  AppBar, ButtonGroup, Checkbox,
57
59
  Dialog, DialogActions, DialogContent, DialogContentText,
58
60
  DialogTitle, Divider, Fab,
61
+ Icon,
62
+ ListItemIcon,
63
+ ListItemText,
59
64
  Menu, MenuItem, Paper, Popover,
60
65
  Table, TableBody, TableCell, TableHead, TableRow, Toolbar
61
66
  } from "@mui/material";
@@ -74,11 +79,23 @@ import {
74
79
  accion_id_pregunta,
75
80
  accion_agregar_formulario,
76
81
  NO_CAMBIAR__VERIFICAR_SI_ES_NECESARIO,
77
- NO_CAMBIAR__SOLO_TRAER_STATUS,
78
- MODO_DM_LOCALSTORAGE_KEY
82
+ NO_CAMBIAR__SOLO_TRAER_STATUS
79
83
  } from "./bypass-formulario"
80
84
 
81
85
  import { html, HtmlTag } from "js-to-html";
86
+ import { FormRenderer, getFormRenderer, registrarCargarMotor } from "./render-config";
87
+
88
+ // Registra la implementación de cargarMotor al cargar el módulo.
89
+ // Esto evita la dependencia circular render-config ← bypass-formulario.
90
+ registrarCargarMotor(async function cargarEstructuraYDatosEnMemoria() {
91
+ var formRenderer = getFormRenderer();
92
+ const estructura = await formRenderer.leerEstructura();
93
+ const datos = await formRenderer.leerDatos();
94
+ if(estructura && datos){
95
+ setEstructura(estructura);
96
+ setDatosByPass(datos);
97
+ }
98
+ });
82
99
 
83
100
  const DELAY_SCROLL_3 = 50;
84
101
 
@@ -218,7 +235,8 @@ const TextField = (props: {
218
235
  const Typography = ({ children, ...others }: {
219
236
  children: any,
220
237
  component?: string
221
- variant?: 'h6'
238
+ variant?: 'body2'|'h5'|'h6'
239
+ color?: ColorValues
222
240
  } & CommonAttributes) => React.createElement(others.variant || others.component || 'div', others, children);
223
241
 
224
242
  function Grid(props: {
@@ -247,6 +265,42 @@ function Grid(props: {
247
265
  >{children}</div>
248
266
  }
249
267
 
268
+ interface FastSetupProps {
269
+ disabled?: boolean;
270
+ children: React.ReactNode;
271
+ }
272
+
273
+ export function FastSetup(props: FastSetupProps): JSX.Element {
274
+ const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
275
+ const [open, setOpen] = React.useState(false);
276
+
277
+ const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
278
+ setAnchorEl(event.currentTarget);
279
+ setOpen((prev) => !prev);
280
+ };
281
+
282
+ const handleClose = () => {
283
+ setOpen(false);
284
+ };
285
+
286
+ return (
287
+ <>
288
+ <Button onClick={handleClick} disabled={props.disabled}>
289
+ <ICON.Settings />
290
+ </Button>
291
+
292
+ <Menu
293
+ open={open}
294
+ anchorEl={anchorEl}
295
+ onClose={handleClose}
296
+ onClick={handleClose}
297
+ >
298
+ {props.children}
299
+ </Menu>
300
+ </>
301
+ );
302
+ }
303
+
250
304
  var p12 = 'p12' as IdVariable;
251
305
  var sp2 = 'sp2' as IdVariable;
252
306
  var sp3 = 'sp3' as IdVariable;
@@ -1558,8 +1612,7 @@ function useSelectorVivienda(forPk: ForPk) {
1558
1612
  var estructura = getEstructura();
1559
1613
  return {
1560
1614
  formulario: estructura.formularios[forPk.formulario].casilleros,
1561
- modoDespliegue: true || state.modo.demo ? state.opciones.modoDespliegue : 'relevamiento',
1562
- modo: state.modo,
1615
+ modoDespliegue: state.opciones.modoDespliegue,
1563
1616
  opciones: state.opciones,
1564
1617
  }
1565
1618
  })
@@ -1690,44 +1743,6 @@ function MenuLetra(props: { tamannio: number, denominacion: string }) {
1690
1743
  >letra {props.denominacion}</MenuItem>
1691
1744
  }
1692
1745
 
1693
- function FastSettup() {
1694
- const [anchorEl, setAnchorEl] = React.useState(null);
1695
- const [open, setOpen] = React.useState(false);
1696
- const handleClick = (event: any) => {
1697
- setAnchorEl(event.currentTarget);
1698
- setOpen((prev) => !prev);
1699
- };
1700
- const dispatch = useDispatch();
1701
- const cambiar = (modoDespliegue: ModoDespliegue) => {
1702
- dispatch(dispatchers.MODO_DESPLIEGUE({ modoDespliegue }));
1703
- setOpen(false)
1704
- }
1705
- const opciones = useSelector((state: CasoState) => state.opciones)
1706
- return <>
1707
- <Button onClick={handleClick}>
1708
- <ICON.Settings />
1709
- </Button>
1710
- <Menu open={open} anchorEl={anchorEl} onClose={() => setOpen(false)}>
1711
- <MenuItem onClick={() => cambiar("relevamiento")}>normal</MenuItem>
1712
- <MenuItem onClick={() => cambiar("PDF")}>PDF para relevamiento</MenuItem>
1713
- <MenuItem onClick={() => cambiar("metadatos")}>revisar metadatos</MenuItem>
1714
- <Divider />
1715
- <MenuLetra tamannio={12} denominacion="muy chica" />
1716
- <MenuLetra tamannio={14} denominacion="chica" />
1717
- <MenuLetra tamannio={16} denominacion="normal" />
1718
- <MenuLetra tamannio={19} denominacion="grande" />
1719
- <MenuLetra tamannio={22} denominacion="enorme" />
1720
- <Divider />
1721
- <MenuItem><label><Checkbox checked={opciones.conCampoOpciones} onChange={
1722
- () => dispatch(dispatchers.SET_OPCION({ opcion: 'conCampoOpciones', valor: !opciones.conCampoOpciones }))
1723
- } inputProps={{ 'aria-label': 'primary checkbox' }} />campo opciones</label></MenuItem>
1724
- <MenuItem><label><Checkbox checked={opciones.saltoAutomatico} onChange={
1725
- () => dispatch(dispatchers.SET_OPCION({ opcion: 'saltoAutomatico', valor: !opciones.saltoAutomatico }))
1726
- } inputProps={{ 'aria-label': 'primary checkbox' }} />salto automático</label></MenuItem>
1727
- </Menu>
1728
- </>;
1729
- }
1730
-
1731
1746
  function BarraDeNavegacion(props: { forPk: ForPk, soloLectura: boolean, modoDirecto: boolean }) {
1732
1747
  const dispatch = useDispatch();
1733
1748
  const forPk = props.forPk;
@@ -1749,7 +1764,7 @@ function BarraDeNavegacion(props: { forPk: ForPk, soloLectura: boolean, modoDire
1749
1764
  }
1750
1765
  var botonesFormulario = [];
1751
1766
  if (!opciones.modoDirecto) {
1752
- botonesFormulario.push({ que: 'hdr', abr: 'HdR', id: ID_BOTON_VOLVER_HDR, label: 'hoja de ruta', retroceso: 0 })
1767
+ botonesFormulario.push({ que: 'hdr', abr: 'HdR', id: ID_BOTON_VOLVER_HDR, label: 'Hoja de Ruta', retroceso: 0 })
1753
1768
  }
1754
1769
  opciones.pilaForPk.forEach((forPk, i) =>
1755
1770
  botonesFormulario.push({ que: 'volver', abr: forPk.formulario.replace(/^F:/, ''), label: forPk.formulario, retroceso: opciones.pilaForPk.length - i })
@@ -1767,6 +1782,9 @@ function BarraDeNavegacion(props: { forPk: ForPk, soloLectura: boolean, modoDire
1767
1782
  elemento.setAttribute('resumen-estado', calcularResumenVivienda(forPk, feedbackAll, r).resumenEstado);
1768
1783
  }
1769
1784
  })
1785
+ const cambiar = (modoDespliegue: ModoDespliegue) => {
1786
+ dispatch(dispatchers.MODO_DESPLIEGUE({ modoDespliegue }));
1787
+ }
1770
1788
  return <>
1771
1789
  <ButtonGroup key="formularios" className="barra-navegacion" solo-lectura={props.soloLectura ? 'si' : 'no'} >
1772
1790
  {botonesFormulario.map((b, i) =>
@@ -1784,6 +1802,7 @@ function BarraDeNavegacion(props: { forPk: ForPk, soloLectura: boolean, modoDire
1784
1802
  }}
1785
1803
  >
1786
1804
  <span className="abr">{b.abr}</span>
1805
+ {b.que == 'hdr' ? <ICON.Assignment /> : null}
1787
1806
  <span className="label">{b.label}</span>
1788
1807
  </Button>
1789
1808
  )}
@@ -1861,7 +1880,24 @@ function BarraDeNavegacion(props: { forPk: ForPk, soloLectura: boolean, modoDire
1861
1880
  <div key={k}><span>{k}</span><span>{v}</span></div>
1862
1881
  ).array()}
1863
1882
  </Typography>
1864
- <FastSettup />
1883
+ <FastSetup>
1884
+ <MenuItem onClick={() => cambiar("relevamiento")}>normal</MenuItem>
1885
+ <MenuItem onClick={() => cambiar("PDF")}>PDF para relevamiento</MenuItem>
1886
+ <MenuItem onClick={() => cambiar("metadatos")}>revisar metadatos</MenuItem>
1887
+ <Divider />
1888
+ <MenuLetra tamannio={12} denominacion="muy chica" />
1889
+ <MenuLetra tamannio={14} denominacion="chica" />
1890
+ <MenuLetra tamannio={16} denominacion="normal" />
1891
+ <MenuLetra tamannio={19} denominacion="grande" />
1892
+ <MenuLetra tamannio={22} denominacion="enorme" />
1893
+ <Divider />
1894
+ <MenuItem><label><Checkbox checked={opciones.conCampoOpciones} onChange={
1895
+ () => dispatch(dispatchers.SET_OPCION({ opcion: 'conCampoOpciones', valor: !opciones.conCampoOpciones }))
1896
+ } inputProps={{ 'aria-label': 'primary checkbox' }} />campo opciones</label></MenuItem>
1897
+ <MenuItem><label><Checkbox checked={opciones.saltoAutomatico} onChange={
1898
+ () => dispatch(dispatchers.SET_OPCION({ opcion: 'saltoAutomatico', valor: !opciones.saltoAutomatico }))
1899
+ } inputProps={{ 'aria-label': 'primary checkbox' }} />salto automático</label></MenuItem>
1900
+ </FastSetup>
1865
1901
  </>
1866
1902
  }
1867
1903
 
@@ -1888,7 +1924,7 @@ function BotonVolverEnDiv({ id }: { id: string }) {
1888
1924
 
1889
1925
  function FormularioDespliegue(props: { forPk: ForPk }) {
1890
1926
  var forPk = props.forPk;
1891
- var { formulario, modoDespliegue, modo, opciones }
1927
+ var { formulario, modoDespliegue, opciones }
1892
1928
  = useSelectorVivienda(props.forPk);
1893
1929
  var soloLectura = getDatosByPass().soloLectura;
1894
1930
  const dispatch = useDispatch();
@@ -1948,28 +1984,20 @@ function FormularioDespliegue(props: { forPk: ForPk }) {
1948
1984
  })
1949
1985
  // @ts-expect-error especial hay que leerlo en el parser de casilleros si esto termina quedando así
1950
1986
  var pantallaCompleta = formulario.especial?.pantallaCompleta;
1951
- const modoDM: ModoDM = my.getLocalVar(MODO_DM_LOCALSTORAGE_KEY);
1987
+ const modoDM: ModoDM = getFormRenderer().getModoDM();
1952
1988
  return (
1953
1989
  <>
1954
- <AppBar position="fixed" color={soloLectura ? 'secondary' : (modoDM == 'capa' ? 'success' : 'primary')}>
1955
- <Toolbar>
1990
+ <AppBarPrincipal
1991
+ modoDM={modoDM}
1992
+ soloLectura={soloLectura}
1993
+ barraNavFormulario={
1956
1994
  <BarraDeNavegacion forPk={forPk} soloLectura={soloLectura || false} modoDirecto={opciones.modoDirecto} />
1957
- <Typography><span style={{ marginLeft: '5px' }}>{modoDM == 'capa' ? ' MODO CAPACITACIÓN' : ''}</span></Typography>
1958
- </Toolbar>
1995
+ }
1996
+ >
1959
1997
  <div id="mini-console"></div>
1960
- </AppBar>
1998
+ </AppBarPrincipal>
1961
1999
  <main>
1962
2000
  <Paper className="formulario" modo-despliegue={modoDespliegue} ver-num-opciones={opciones.conCampoOpciones ? 'SI' : 'NO'}>
1963
- {modo.demo ? <div>
1964
- <Typography component="span">Modo de despliegue:</Typography>
1965
- <ButtonGroup>
1966
- {listaModos.map(modo =>
1967
- <Button key={modo} variant={modo == modoDespliegue ? "contained" : "outlined"} onClick={
1968
- () => dispatch(dispatchers.MODO_DESPLIEGUE({ modoDespliegue: modo }))
1969
- }>{modo}</Button>
1970
- )}
1971
- </ButtonGroup>
1972
- </div> : null}
1973
2001
  {pantallaCompleta ? null : <BotonVolverEnDiv id="boton-volver-1" />}
1974
2002
  {pantallaCompleta ? null : <FormularioEncabezado casillero={formulario} />}
1975
2003
  <DesplegarContenidoInternoBloqueOFormulario bloqueOFormulario={formulario} formulario={formulario} forPk={forPk} multiple={false} />
@@ -2110,12 +2138,11 @@ setDesplegarCarga((props: {
2110
2138
  [formulario in PlainForPk]: FormStructureState<IdVariable, Valor, IdFin>
2111
2139
  }
2112
2140
  }) => {
2113
- const [newSurvey, setNewSurvey] = useState(0);
2114
2141
  const { carga, idCarga, informacionHdr, respuestas } = props;
2115
2142
  var estructura = getEstructura();
2116
2143
  let cantLineasResumen = likeAr(informacionHdr).filter((informacion) => informacion.tem.carga == idCarga).array().length;
2117
2144
  const dispatch = useDispatch();
2118
- const modoDM: ModoDM = my.getLocalVar(MODO_DM_LOCALSTORAGE_KEY);
2145
+ const modoDM: ModoDM = getFormRenderer().getModoDM();
2119
2146
  return <Paper className="carga" style={{ marginBottom: '10px', padding: '10px' }}>
2120
2147
  <div className="informacion-carga">
2121
2148
  <div className="carga">Área: {idCarga}</div>
@@ -2232,66 +2259,496 @@ export function DesplegarTem(props: { tem: TEM }) {
2232
2259
  </div>
2233
2260
  }
2234
2261
 
2235
- export type HojaDeRutaDespliegueType = (props: {}) => JSX.Element;
2262
+ export const handleLogout = async (): Promise<void> => {
2263
+ await getFormRenderer().onLogout();
2264
+ };
2265
+
2266
+ export type AppBarPrincipalProps = {
2267
+ titulo?: React.ReactNode;
2268
+ barraNavFormulario?: React.ReactNode;
2269
+ modoDM: ModoDM;
2270
+ soloLectura?: boolean;
2271
+ children?: React.ReactNode;
2272
+ disabled?: boolean;
2273
+ };
2274
+
2275
+ export function AppBarPrincipal(props: AppBarPrincipalProps): JSX.Element {
2276
+ var color: 'secondary' | 'success' | 'primary' = props.soloLectura ? 'secondary' : props.modoDM === 'capa' ? 'success' : 'primary';
2277
+ var { forPk, pantallaActual } = useSelector((state: CasoState) => state.opciones);
2278
+ const mostrarBotoneraNavegacion = pantallaActual && pantallaActual !== 'sincronizacion_requerida' && !props.barraNavFormulario;
2279
+ return (
2280
+ <AppBar position="fixed" color={color}
2281
+ style={props.disabled ? { pointerEvents: 'none', opacity: 0.9 } : {}}
2282
+ >
2283
+ <Toolbar>
2284
+ {props.barraNavFormulario}
2285
+ {mostrarBotoneraNavegacion ? (
2286
+ <BotoneraNavegacion disabled={props.disabled}/>
2287
+ ) : null}
2288
+ {props.modoDM === 'capa' && !props.barraNavFormulario ? (
2289
+ <Typography><span style={{ marginLeft: '5px' }}> MODO CAPACITACIÓN</span></Typography>
2290
+ ) : null}
2291
+ <UsuarioLogueadoYConfig mostrarSetup={!forPk} mostrarLogout={!!pantallaActual && !props.barraNavFormulario} onLogout={handleLogout} />
2292
+ </Toolbar>
2293
+ {props.children}
2294
+ </AppBar>
2295
+ );
2296
+ }
2236
2297
 
2237
- export var HojaDeRutaDespliegue: HojaDeRutaDespliegueType
2298
+ export function BotoneraNavegacion(props: { disabled?: boolean }): JSX.Element {
2299
+ const dispatch = useDispatch();
2300
+ const { pantallaActual } = useSelector((state: CasoState) => state.opciones);
2301
+ return (
2302
+ <ButtonGroup variant="outlined" size="small" color="inherit">
2303
+ <Button
2304
+ onClick={() =>
2305
+ dispatch(dispatchers.SET_OPCION({ opcion: 'pantallaActual', valor: 'hdr' }))
2306
+ }
2307
+ disabled={pantallaActual === 'hdr' || props.disabled}
2308
+ style={pantallaActual === 'hdr' ? { backgroundColor: 'rgba(255,255,255,0.1)' } : undefined}
2309
+ >
2310
+ <ICON.Assignment /> Hoja de Ruta
2311
+ </Button>
2312
+
2313
+ <Button
2314
+ onClick={() =>
2315
+ dispatch(dispatchers.SET_OPCION({ opcion: 'pantallaActual', valor: 'sincronizacion' }))
2316
+ }
2317
+ disabled={pantallaActual === 'sincronizacion' || props.disabled}
2318
+ style={pantallaActual === 'sincronizacion' ? { backgroundColor: 'rgba(255,255,255,0.1)' } : undefined}
2319
+ >
2320
+ <ICON.CloudSync /> Sincronización
2321
+ </Button>
2322
+ </ButtonGroup>
2323
+ );
2324
+ }
2238
2325
 
2239
- export const setHojaDeRutaDespliegue = (hojaDeRuta: HojaDeRutaDespliegueType) => HojaDeRutaDespliegue = hojaDeRuta
2326
+ export function UsuarioLogueadoYConfig(props: {
2327
+ mostrarSetup?: boolean,
2328
+ mostrarLogout?: boolean,
2329
+ onLogout?: () => void,
2330
+ navigationMenu?: React.ReactNode
2331
+ }): JSX.Element {
2332
+ var username: string | null = getFormRenderer().getUsernameLogueado();
2333
+ var idper: string | null = getFormRenderer().getIdperLogueado();
2334
+ var { pantallaActual } = useSelector((state: CasoState) => state.opciones);
2335
+
2336
+ var dispatch = useDispatch();
2337
+ var [openDialog, setOpenDialog] = useState<boolean>(false);
2338
+
2339
+ var handleLogoutClick = (): void => {
2340
+ setOpenDialog(true);
2341
+ };
2240
2342
 
2241
- setHojaDeRutaDespliegue((_props: {}) => {
2242
- var { cargas, num_sincro, informacionHdr, respuestas } = getDatosByPass();
2243
- var { modo } = useSelector((state: CasoState) => ({ modo: state.modo }));
2244
- var feedbackRowValidator = getFeedbackRowValidator()
2245
- const dispatch = useDispatch();
2246
- const online = useOnlineStatus();
2247
- const modoDM: ModoDM = my.getLocalVar(MODO_DM_LOCALSTORAGE_KEY);
2343
+ var handleClose = (): void => {
2344
+ setOpenDialog(false);
2345
+ };
2346
+
2347
+ var handleConfirmLogout = async (): Promise<void> => {
2348
+ setOpenDialog(false);
2349
+ if (props.onLogout) {
2350
+ await props.onLogout();
2351
+ }
2352
+ };
2353
+
2354
+ var datos = getDatosByPass();
2355
+ var tieneEncuestas: boolean = !!(datos && datos.informacionHdr && Object.keys(datos.informacionHdr).length > 0);
2356
+ const modoDM = getFormRenderer().getModoDM();
2248
2357
  return (
2249
- <>
2250
- <AppBar position="fixed" color={modoDM == 'capa' ? 'success' : 'primary'}>
2251
- <Toolbar>
2252
- <Typography>
2253
- HOJA DE RUTA
2254
- </Typography>
2255
- {
2256
- //<IconButton style={{marginTop:'3px'}}
2257
- // color="inherit"
2258
- // //onClick={/*dispatch que lleva a pantalla opciones*/}
2259
- //>
2260
- // <ICON.Settings/>
2261
- //</IconButton>
2262
- }
2263
- {online ?
2358
+ <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
2359
+ {props.navigationMenu}
2360
+ <Typography variant="body2" style={{ color: 'inherit', fontWeight: 'bold', marginLeft:'5px' }}>
2361
+ {idper} - {username}
2362
+ </Typography>
2363
+ {props.mostrarSetup ? (
2364
+ <FastSetup>
2365
+ <MenuItem
2366
+ onClick={() =>
2367
+ dispatch(dispatchers.SET_OPCION({ opcion: 'pantallaActual', valor: 'modo' }))}
2368
+ >
2369
+ <ListItemIcon>{modoDM === 'capa' ? <ICON.BusinessCenter /> : <ICON.SportsEsports />} </ListItemIcon>
2370
+ <ListItemText>Cambio a modo {modoDM === 'capa' ? 'Producción' : 'Capacitación'}</ListItemText>
2371
+ </MenuItem>
2372
+ <Divider/>
2373
+ <MenuItem
2374
+ disabled={true}
2375
+ >
2376
+ <ListItemText>Soporte técnico</ListItemText>
2377
+ </MenuItem>
2378
+ <MenuItem
2379
+ onClick={() =>
2380
+ dispatch(dispatchers.RESET_OPCIONES({}))}
2381
+ >
2382
+ <ListItemIcon><ICON.Refresh/></ListItemIcon>
2383
+ <ListItemText>Reset opciones</ListItemText>
2384
+ </MenuItem>
2385
+ <MenuItem
2386
+ onClick={() => {
2387
+ history.replaceState(null, '', `${location.origin + location.pathname}/../menu#i=sincronizar`);
2388
+ location.reload();
2389
+ }}
2390
+ >
2391
+ <ListItemIcon><ICON.SyncAlt /></ListItemIcon>
2392
+ <ListItemText>Sincronización (link viejo)</ListItemText>
2393
+ </MenuItem>
2394
+ </FastSetup>
2395
+ ) : null}
2396
+ {props.mostrarLogout ? (
2397
+ <Button color="inherit" onClick={handleLogoutClick} style={{ minWidth: 'auto', padding: '6px' }}>
2398
+ <ICON.ExitToApp />
2399
+ </Button>
2400
+ ) : null}
2401
+
2402
+ <Dialog open={openDialog} onClose={handleClose}>
2403
+ <DialogTitle>Cerrar Sesión</DialogTitle>
2404
+ <DialogContent>
2405
+ <DialogContentText>
2406
+ {tieneEncuestas
2407
+ ? "Hay encuestas cargadas en el dispositivo. Si sale de la aplicación sin sincronizar podría perder el trabajo realizado."
2408
+ : "¿Está seguro de que desea salir? Para volver a ingresar deberá iniciar sesión nuevamente."
2409
+ }
2410
+ </DialogContentText>
2411
+ </DialogContent>
2412
+ <DialogActions>
2413
+ {tieneEncuestas ? (
2264
2414
  <>
2265
- <Button
2266
- color="inherit"
2267
- variant="outlined"
2268
- style={{ margin: '0 8px' }}
2269
- onClick={() => {
2270
- gotoSincronizar()
2271
- }}
2272
- >
2273
- <ICON.SyncAlt />
2415
+ <Button onClick={handleConfirmLogout} color="secondary">
2416
+ Salir de todos modos
2417
+ </Button>
2418
+ <Button onClick={handleClose} color="primary" variant="contained">
2419
+ Continuar relevando
2274
2420
  </Button>
2275
2421
  </>
2276
- : null}
2277
- <Typography><span style={{ marginLeft: '5px' }}>{modoDM == 'capa' ? ' MODO CAPACITACIÓN' : ''}</span></Typography>
2278
- </Toolbar>
2279
- </AppBar>
2422
+ ) : (
2423
+ <>
2424
+ <Button onClick={handleClose} color="inherit">
2425
+ Cancelar
2426
+ </Button>
2427
+ <Button onClick={handleConfirmLogout} color="primary" variant="contained">
2428
+ Salir
2429
+ </Button>
2430
+ </>
2431
+ )}
2432
+ </DialogActions>
2433
+ </Dialog>
2434
+ </div>
2435
+ );
2436
+ }
2437
+
2438
+ export interface LayoutAccionDispositivoProps {
2439
+ tituloAppBar: React.ReactNode;
2440
+ tituloPanel: string;
2441
+ headerAdicional?: React.ReactNode;
2442
+
2443
+ modoDM: ModoDM;
2444
+ online: boolean;
2445
+
2446
+ loading?: boolean;
2447
+ aviso?: { tipo: 'info' | 'success' | 'error', mensaje: string } | null;
2448
+
2449
+ textoBotonAccion: React.ReactNode;
2450
+ colorBotonAccion?: ColorValues;
2451
+ onAccion: () => void;
2452
+
2453
+ deshabilitarBotonVolver?: boolean;
2454
+ mensajeVacio?: string;
2455
+
2456
+ childrenModales?: React.ReactNode;
2457
+ }
2458
+
2459
+ export function LayoutAccionDispositivo(props: LayoutAccionDispositivoProps): JSX.Element {
2460
+ const datosByPass = getDatosByPass();
2461
+ const estructura = getEstructura();
2462
+ const dispatch = useDispatch();
2463
+ useEffect(() => {
2464
+ scrollToTop();
2465
+ }, []);
2466
+
2467
+ const avisoRedux = useSelector((state: CasoState) => state.opciones.avisoPersistente);
2468
+ const avisoAMostrar = props.aviso || avisoRedux;
2469
+ useEffect(() => {
2470
+ if (avisoRedux) {
2471
+ const timer = setTimeout(() => {
2472
+ dispatch(dispatchers.SET_OPCION({ opcion: 'avisoPersistente', valor: null }));
2473
+ }, 5000);
2474
+ return () => clearTimeout(timer);
2475
+ }
2476
+ return
2477
+ }, [avisoRedux, dispatch]);
2478
+
2479
+ const cantCargas: number = hayDatosByPassAlmacenados() ? likeAr(datosByPass.cargas).array().length : 0;
2480
+ const cantRespuestas: number = hayDatosByPassAlmacenados() && estructura ? likeAr(datosByPass.respuestas[estructura.uaPpal]).array().length : 0;
2481
+
2482
+ return (
2483
+ <>
2484
+ <AppBarPrincipal
2485
+ modoDM={props.modoDM}
2486
+ titulo={props.tituloAppBar}
2487
+ disabled={props.loading}
2488
+ />
2280
2489
  <div className="hoja-de-ruta">
2281
- <Paper style={{ marginBottom: '10px', padding: '10px' }}>
2282
- {modo.demo ? <div>
2283
- <Typography>Modo demo </Typography>
2284
- <Button variant="outlined" color="secondary"
2285
- onClick={() => dispatch(dispatchers.REINICIAR_DEMO({}))}
2490
+ <InfoVersionSincroDespliegue/>
2491
+ <Paper style={{ padding: '20px', margin: 'auto', marginBottom: '10px' }}>
2492
+ <Typography variant="h5">{props.tituloPanel}</Typography>
2493
+
2494
+ {props.headerAdicional}
2495
+
2496
+ {hayDatosByPassAlmacenados() ? (
2497
+ <div style={{ marginBottom: '20px' }}>
2498
+ <Typography variant="h6">Información del dispositivo:</Typography>
2499
+ <Typography>{cantCargas} áreas: {likeAr(datosByPass.cargas).keys().join(', ')}</Typography>
2500
+ <Typography>{cantRespuestas} {estructura?.uaPpal || 'viviendas'}</Typography>
2501
+ </div>
2502
+ ) : (
2503
+ props.mensajeVacio && props.mensajeVacio.trim() !== "" ? (
2504
+ <Typography style={{ marginBottom: '20px' }}>{props.mensajeVacio}</Typography>
2505
+ ) : null
2506
+ )}
2507
+
2508
+ {avisoAMostrar && (
2509
+ <Paper style={{
2510
+ padding: '10px',
2511
+ marginBottom: '20px',
2512
+ backgroundColor: avisoAMostrar.tipo === 'success' ? '#e6fffa' : avisoAMostrar.tipo === 'error' ? '#fff5f5' : '#edf2f7',
2513
+ color: avisoAMostrar.tipo === 'success' ? '#234e52' : avisoAMostrar.tipo === 'error' ? '#9b2c2c' : '#2d3748',
2514
+ border: `1px solid ${avisoAMostrar.tipo === 'success' ? '#319795' : avisoAMostrar.tipo === 'error' ? '#e53e3e' : '#cbd5e0'}`
2515
+ }}>
2516
+ <Typography>{avisoAMostrar.mensaje}</Typography>
2517
+ </Paper>
2518
+ )}
2519
+
2520
+ <div style={{ display: 'flex', gap: '10px', alignItems: 'center', flexWrap: 'wrap' }}>
2521
+ <Button
2522
+ variant="contained"
2523
+ color={props.colorBotonAccion || 'primary'}
2524
+ disabled={props.loading || !props.online}
2525
+ onClick={props.onAccion}
2286
2526
  >
2287
- reiniciar
2288
- </Button>
2289
- </div> : null}
2290
- <div className="nombre-version">
2291
- <div>Instituto de Estadística y Censos de la Ciudad Autónoma de Buenos Aires - IDECBA</div>
2292
- <div>versión {my.getLocalVar('app-cache-version')} - sincro {num_sincro}</div>
2527
+ {props.textoBotonAccion}
2528
+ </Button>
2529
+ </div>
2530
+ <div style={{ display: 'flex', gap: '10px', alignItems: 'center', flexWrap: 'wrap' }}>
2531
+ {!props.online && (
2532
+ <Typography color="secondary" style={{ marginLeft: props.deshabilitarBotonVolver ? '0px' : '10px' }}>
2533
+ Debe estar conectado a internet para realizar esta acción.
2534
+ </Typography>
2535
+ )}
2293
2536
  </div>
2294
2537
  </Paper>
2538
+ </div>
2539
+
2540
+ {props.childrenModales}
2541
+ </>
2542
+ );
2543
+ }
2544
+
2545
+ interface PantallaSincronizacionProps {
2546
+ // Props opcionales para el "sabor" requerida
2547
+ requerida?: boolean;
2548
+ mensajeRequerido?: React.ReactNode;
2549
+ }
2550
+
2551
+ export function PantallaSincronizacion(props: PantallaSincronizacionProps): JSX.Element {
2552
+ const [loading, setLoading] = useState<boolean>(false);
2553
+ const avisoPersistente = useSelector((state: CasoState) => state.opciones.avisoPersistente);
2554
+ const online: boolean = useOnlineStatus();
2555
+ const dispatch = useDispatch();
2556
+ const modoDM = getFormRenderer().getModoDM();
2557
+
2558
+ const handleSincronizar = async (): Promise<void> => {
2559
+ setLoading(true);
2560
+ try {
2561
+ dispatch(dispatchers.SET_OPCION({ opcion: 'avisoPersistente', valor: null }));
2562
+ const formRenderer = getFormRenderer();
2563
+ const datos = await formRenderer.onSincronizar(await formRenderer.leerDatos(), false);
2564
+ await formRenderer.cargarMotor();
2565
+ const mensajeExito = `Sincronización exitosa. Sincro Nº ${datos.num_sincro}`;
2566
+ dispatch(dispatchers.SET_PANTALLA_ACTUAL({ pantalla: 'sincronizacion', avisoPersistente: { tipo: 'success', mensaje: mensajeExito } }));
2567
+ } catch (err) {
2568
+ console.log(err)
2569
+ dispatch(dispatchers.SET_PANTALLA_ACTUAL({
2570
+ pantalla: props.requerida ? 'sincronizacion_requerida' : 'sincronizacion',
2571
+ avisoPersistente: { tipo: 'error', mensaje: `Error de sincronización: ${unexpected(err).message}` }
2572
+ }));
2573
+ } finally {
2574
+ setLoading(false);
2575
+ }
2576
+ };
2577
+
2578
+ // Estilo para el mensaje cuando es requerida
2579
+ const paragraphStyles = { fontSize: "1.2rem", fontWeight: 600, padding: "5px 10px", marginBottom: "15px" };
2580
+
2581
+ // Si es requerida, inyectamos el mensaje explicativo arriba
2582
+ const headerAdicional = props.requerida && props.mensajeRequerido ? (
2583
+ <Typography component="div" style={paragraphStyles}>
2584
+ {props.mensajeRequerido}
2585
+ </Typography>
2586
+ ) : null;
2587
+
2588
+ return (
2589
+ <LayoutAccionDispositivo
2590
+ tituloAppBar={props.requerida ? `SINCRONIZACIÓN REQUERIDA${modoDM === 'capa' ? ' - CAPA' : ''}` : 'SINCRONIZACIÓN'}
2591
+ modoDM={modoDM}
2592
+ tituloPanel={props.requerida ? "Sincronización Requerida" : "Sincronizar Dispositivo"}
2593
+ headerAdicional={headerAdicional}
2594
+ aviso={avisoPersistente}
2595
+ loading={loading}
2596
+ online={online}
2597
+ textoBotonAccion={
2598
+ loading ?
2599
+ 'Sincronizando...'
2600
+ :
2601
+ <span style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><ICON.CloudSync /> Sincronizar</span>
2602
+ }
2603
+ colorBotonAccion={modoDM === 'capa' ? 'success' : 'primary'}
2604
+ onAccion={handleSincronizar}
2605
+ mensajeVacio={props.requerida ? " " : "No hay información de formularios localmente."}
2606
+ />
2607
+ );
2608
+ }
2609
+
2610
+ export function PantallaCambioModo(): JSX.Element {
2611
+ const [loading, setLoading] = useState<boolean>(false);
2612
+ const [aviso, setAviso] = useState<{ tipo: 'info' | 'success' | 'error', mensaje: string } | null>(null);
2613
+ const [password, setPassword] = useState<string>('');
2614
+ const [dialogOpen, setDialogOpen] = useState<boolean>(false);
2615
+ const [modoDM, setModoDM] = useState<ModoDM>('produc');
2616
+ const [errorPassword, setErrorPassword] = useState<string | null>(null);
2617
+ const online: boolean = useOnlineStatus();
2618
+ const dispatch = useDispatch();
2619
+
2620
+ useEffect(() => {
2621
+ const fetchModo = async (): Promise<void> => {
2622
+ const m: ModoDM = getFormRenderer().getModoDM();
2623
+ getFormRenderer().setModoDM(m);
2624
+ setModoDM(m);
2625
+ };
2626
+ fetchModo();
2627
+ }, []);
2628
+
2629
+ const handleConfirmarCambio = async (): Promise<void> => {
2630
+ if (password !== '1234') {
2631
+ setErrorPassword('Contraseña incorrecta.');
2632
+ return;
2633
+ }
2634
+ setErrorPassword(null);
2635
+ setDialogOpen(false);
2636
+ setLoading(true);
2637
+ setAviso(null);
2638
+ try {
2639
+ const nuevoModo: ModoDM = modoDM === 'produc' ? 'capa' : 'produc';
2640
+ const formRenderer = getFormRenderer();
2641
+ await formRenderer.onSincronizar(await formRenderer.leerDatos(), true);
2642
+ formRenderer.setModoDM(nuevoModo);
2643
+ setModoDM(nuevoModo);
2644
+ await formRenderer.cargarMotor();
2645
+ setAviso({ tipo: 'success', mensaje: `MODO ${nuevoModo.toUpperCase()} ACTIVADO Y DISPOSITIVO SINCRONIZADO` });
2646
+ dispatch(dispatchers.SET_OPCION({ opcion: 'pantallaActual', valor: 'modo' }));
2647
+ } catch (err) {
2648
+ setAviso({ tipo: 'error', mensaje: `Error al cambiar de modo: ${unexpected(err).message}` });
2649
+ } finally {
2650
+ setLoading(false);
2651
+ setPassword('');
2652
+ }
2653
+ };
2654
+
2655
+ const headerAdicionalText = (
2656
+ <Typography style={{ marginBottom: '20px', fontWeight: 'bold' }}>
2657
+ Modo actual: <span style={{ color: modoDM === 'capa' ? 'green' : 'blue', textTransform: 'uppercase' }}>{modoDM}</span>
2658
+ </Typography>
2659
+ );
2660
+
2661
+ return (
2662
+ <LayoutAccionDispositivo
2663
+ tituloAppBar="CAMBIO DE MODO"
2664
+ modoDM={modoDM}
2665
+ tituloPanel="Cambiar Modo del Dispositivo"
2666
+ headerAdicional={headerAdicionalText}
2667
+ aviso={aviso}
2668
+ loading={loading}
2669
+ online={online}
2670
+ textoBotonAccion={`Cambiar a modo ${modoDM === 'produc' ? 'capa' : 'produc'}`}
2671
+ colorBotonAccion={modoDM === 'produc' ? 'primary' : 'success'}
2672
+ onAccion={() => { setErrorPassword(null); setDialogOpen(true); }}
2673
+ childrenModales={
2674
+ <Dialog open={dialogOpen} onClose={() => { setDialogOpen(false); setPassword(''); setErrorPassword(null); }}>
2675
+ <DialogTitle>Confirmar cambio de modo</DialogTitle>
2676
+ <DialogContent>
2677
+ <DialogContentText style={{ marginBottom: '15px' }}>
2678
+ Confirma cambio de modo "{modoDM}" a "{modoDM === 'produc' ? 'capa' : 'produc'}". Se sincronizará el dispositivo.
2679
+ </DialogContentText>
2680
+ {errorPassword && (
2681
+ <Typography style={{ color: '#d32f2f', marginBottom: '15px', fontWeight: 'bold' }}>
2682
+ {errorPassword}
2683
+ </Typography>
2684
+ )}
2685
+ <Typography variant="body2" style={{ marginBottom: '10px' }}>Por favor ingrese la contraseña:</Typography>
2686
+ <input
2687
+ type="password"
2688
+ value={password}
2689
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
2690
+ setPassword(e.target.value);
2691
+ if (errorPassword) {
2692
+ setErrorPassword(null);
2693
+ }
2694
+ }}
2695
+ style={{ width: '100%', padding: '10px', fontSize: '16px' }}
2696
+ />
2697
+ </DialogContent>
2698
+ <DialogActions>
2699
+ <Button onClick={() => { setDialogOpen(false); setPassword(''); setErrorPassword(null); }} color="inherit">
2700
+ Cancelar
2701
+ </Button>
2702
+ <Button onClick={handleConfirmarCambio} color="primary" variant="contained">
2703
+ Confirmar
2704
+ </Button>
2705
+ </DialogActions>
2706
+ </Dialog>
2707
+ }
2708
+ />
2709
+ );
2710
+ }
2711
+
2712
+ interface InfoVersionSincroDespliegueProps {
2713
+ }
2714
+
2715
+ export function InfoVersionSincroDespliegue(_props: InfoVersionSincroDespliegueProps): JSX.Element {
2716
+ const version = getFormRenderer().getAppCacheVersion();
2717
+ const { num_sincro } = getDatosByPass();
2718
+ return (
2719
+ <Paper style={{ marginBottom: '10px', padding: '10px' }}>
2720
+ <div className="nombre-version">
2721
+ <div>Instituto de Estadística y Censos de la Ciudad Autónoma de Buenos Aires - IDECBA</div>
2722
+ <div>versión {version} - {num_sincro ? `sincro: ${num_sincro.toString()}` : 'sin sincronizar'}</div>
2723
+ </div>
2724
+ </Paper>
2725
+ );
2726
+ }
2727
+
2728
+ export type HojaDeRutaDespliegueProps = {
2729
+ };
2730
+
2731
+ export type HojaDeRutaDespliegueType = (props: HojaDeRutaDespliegueProps) => JSX.Element;
2732
+
2733
+ export var HojaDeRutaDespliegue: HojaDeRutaDespliegueType
2734
+
2735
+ export const setHojaDeRutaDespliegue = (hojaDeRuta: HojaDeRutaDespliegueType) => HojaDeRutaDespliegue = hojaDeRuta
2736
+
2737
+ setHojaDeRutaDespliegue((_props: HojaDeRutaDespliegueProps) => {
2738
+ useEffect(() => {
2739
+ scrollToTop();
2740
+ }, []);
2741
+ var { cargas, num_sincro, informacionHdr, respuestas } = getDatosByPass();
2742
+ var feedbackRowValidator = getFeedbackRowValidator();
2743
+ const modoDM: ModoDM = getFormRenderer().getModoDM();
2744
+ return (
2745
+ <>
2746
+ <AppBarPrincipal
2747
+ modoDM={modoDM}
2748
+ titulo="HOJA DE RUTA"
2749
+ />
2750
+ <div className="hoja-de-ruta">
2751
+ <InfoVersionSincroDespliegue/>
2295
2752
  {likeAr(cargas).map((carga, idCarga, _, posicion) =>
2296
2753
  <DesplegarCarga key={idCarga} carga={carga} idCarga={idCarga} posicion={posicion} informacionHdr={informacionHdr} feedbackRowValidator={feedbackRowValidator} respuestas={respuestas} />
2297
2754
  ).array()}
@@ -2306,41 +2763,6 @@ export function ListaTextos(props: { textos: string[] }) {
2306
2763
  </ul>;
2307
2764
  }
2308
2765
 
2309
- export function BienvenidaDespliegue(props: { modo: CasoState["modo"] }) {
2310
- const dispatch = useDispatch();
2311
- return <Paper className="bienvenida">
2312
- {props.modo.demo ?
2313
- <>
2314
- <Typography>DEMO del sistema de relevamiento de DMENCU</Typography>
2315
- <Typography>En esta demo:</Typography>
2316
- <ListaTextos textos={[
2317
- "Algunas viviendas aparecen relevadas (el botón está de color) sirven de ejemplo",
2318
- "Lo que se carguen se guardan localmente pero no se trasmiten a la base de datos",
2319
- "Se puede volver a la versión inicial (o sea borrar lo que se guardó localmente) desde la hoja de ruta boton [reiniciar demo]",
2320
- "Todavía hay cosas que faltan o pueden cambiar",
2321
- ]} />
2322
- </>
2323
- : <>
2324
- <Typography>Encuesta de Seroprevalencia de COVID-19</Typography>
2325
- </>
2326
- }
2327
- <Button
2328
- variant="contained"
2329
- color="secondary"
2330
- onClick={() => { gotoSincronizar(); }}
2331
- >
2332
- <span>Sincronizar </span> <ICON.SyncAlt />
2333
- </Button>
2334
- <Button
2335
- variant="contained"
2336
- color="primary"
2337
- onClick={() => dispatch(dispatchers.SET_OPCION({ opcion: 'bienvenido', valor: true }))}
2338
- >
2339
- <span>Continuar </span> <ICON.Send />
2340
- </Button>
2341
- </Paper>
2342
- }
2343
-
2344
2766
  //CONTROL DE PESTAÑAS
2345
2767
  var allOpenedTabs: { [x: string]: number } = {};
2346
2768
  var infoOpenedTabs = {
@@ -2366,88 +2788,50 @@ export function OpenedTabs() {
2366
2788
  }
2367
2789
 
2368
2790
  export function AppDmEncu() {
2369
- var { forPk, bienvenido, modo } = useSelector((state: CasoState) => ({ ...state.opciones, ...state.modo, ...state }));
2370
- if (!bienvenido) {
2371
- return <BienvenidaDespliegue modo={modo} />
2372
- } else if (forPk == null) {
2373
- return <HojaDeRutaDespliegue />
2374
- } else {
2375
- return <FormularioDespliegue forPk={forPk} />
2376
- }
2377
- }
2791
+ var { forPk, pantallaActual } = useSelector((state: CasoState) => state.opciones);
2792
+ var datosByPass = getDatosByPass();
2793
+ const dispatch = useDispatch();
2378
2794
 
2379
- function PantallaInicialSinCarga(_props: {}) {
2380
- const online = useOnlineStatus();
2795
+ // Determinar si la sincronización es requerida
2796
+ const sincroRequerida = !hayDatosByPassAlmacenados()
2797
+ || (hayDatosByPassAlmacenados() && datosByPass.idper !== getFormRenderer().getIdperLogueado());
2381
2798
 
2382
- const paragraphStyles = { fontSize: "1.2rem", fontWeight: 600, padding: "5px 10px" };
2383
- const modoDM: ModoDM = my.getLocalVar(MODO_DM_LOCALSTORAGE_KEY);
2384
- return (
2385
- <>
2386
- <AppBar position="fixed" color={modoDM == 'capa' ? 'success' : 'primary'}>
2387
- <Typography variant="h6" style={{ margin: 25 }}>
2388
- Dispositivo sin carga - {modoDM == 'capa' ? 'MODO CAPACITACIÓN' : ''}
2389
- </Typography>
2390
- </AppBar>
2391
- <main>
2392
- <Paper style={{ height: '600px', padding: "15px", marginTop: 75 }}>
2393
- <div>
2394
- {online ?
2395
- <>
2396
- <Typography component="p" style={paragraphStyles}>
2397
- Sincronizar dispositivo
2398
- <span style={{ padding: '5px' }}>
2399
- <Button
2400
- color={modoDM == 'capa' ? 'success' : "primary"}
2401
- variant="contained"
2402
- onClick={() => {
2403
- gotoSincronizar()
2404
- }}
2405
- >
2406
- <ICON.SyncAlt />
2407
- </Button>
2408
- </span>
2409
- </Typography>
2410
- </>
2411
- :
2412
- <Typography component="p" style={paragraphStyles}>
2413
- No hay conexión a internet, por favor conécte el dispositivo a una red para sincronizar una hoja de ruta.
2414
- </Typography>
2415
- }
2416
- </div>
2417
- </Paper>
2418
- </main>
2419
- </>
2420
- );
2421
- }
2799
+ // Si la sincro es requerida, forzar pantallaActual vía Redux
2800
+ useEffect(() => {
2801
+ if (sincroRequerida && pantallaActual !== 'sincronizacion_requerida') {
2802
+ dispatch(dispatchers.SET_OPCION({ opcion: 'pantallaActual', valor: 'sincronizacion_requerida' }));
2803
+ }
2804
+ }, [sincroRequerida, pantallaActual, dispatch]);
2805
+
2806
+ if (sincroRequerida) {
2807
+ const mensaje = !hayDatosByPassAlmacenados()
2808
+ ? <> Debe sincronizar el dispositivo para obtener una hoja de ruta.</>
2809
+ : <> El usuario autenticado no coincide con el usuario asociado al
2810
+ trabajo almacenado en la tablet.
2811
+ <br />
2812
+ Debe sincronizar antes de continuar.
2813
+ </>;
2814
+ return <PantallaSincronizacion requerida={true} mensajeRequerido={mensaje} />;
2815
+ }
2422
2816
 
2423
- export function PantallaInicial() {
2424
- var { forPk, bienvenido, modo } = useSelector((state: CasoState) => ({ ...state.opciones, ...state.modo, ...state }));
2425
- if (!bienvenido) {
2426
- return <BienvenidaDespliegue modo={modo} />
2427
- } else if (forPk == null) {
2817
+ if (forPk == null) {
2818
+ if (pantallaActual === 'sincronizacion') {
2819
+ return <PantallaSincronizacion />
2820
+ } else if (pantallaActual === 'modo') {
2821
+ return <PantallaCambioModo />
2822
+ }
2428
2823
  return <HojaDeRutaDespliegue />
2429
2824
  } else {
2430
2825
  return <FormularioDespliegue forPk={forPk} />
2431
2826
  }
2432
2827
  }
2433
2828
 
2434
- export async function dmPantallaInicialSinCarga() {
2435
- try {
2436
- await loadCSS(BOOTSTRAP_5_1_3_SRC);
2437
- } catch (err) {
2438
- throw (err)
2439
- }
2440
- ReactDOM.render(
2441
- <PantallaInicialSinCarga />,
2442
- document.getElementById('main_layout')
2443
- )
2444
- }
2445
-
2446
- export async function desplegarFormularioActual(opts: { modoDemo: boolean, forPkRaiz?: ForPkRaiz, operativo: IdOperativo }) {
2447
- // traer los metadatos en una "estructura"
2448
- // traer los datos de localStorage
2449
- // verificar el main Layout
2450
- const store = await dmTraerDatosFormulario(opts)
2829
+ export async function desplegarFormularioActual(
2830
+ formRenderer: FormRenderer,
2831
+ opts: { forPkRaiz?: ForPkRaiz } = {}
2832
+ ) {
2833
+ await formRenderer.cargarMotor();
2834
+ const store = await crearStoreFormulario(opts)
2451
2835
  try {
2452
2836
  await loadCSS(BOOTSTRAP_5_1_3_SRC);
2453
2837
  } catch (err) {
@@ -2462,14 +2846,6 @@ export async function desplegarFormularioActual(opts: { modoDemo: boolean, forPk
2462
2846
  )
2463
2847
  }
2464
2848
 
2465
- if (typeof window !== 'undefined') {
2466
- // @ts-ignore para hacerlo
2467
- window.desplegarFormularioActual = desplegarFormularioActual;
2468
- // @ts-ignore para hacerlo
2469
- window.dmPantallaInicialSinCarga = dmPantallaInicialSinCarga;
2470
- }
2471
-
2472
-
2473
2849
  function loadInstance() {
2474
2850
  if (typeof BroadcastChannel === 'undefined') {
2475
2851
  return;