diff --git a/.vscode/settings.json b/.vscode/settings.json index 6f63eef..e4a1751 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,31 @@ "editor.defaultFormatter": "dbaeumer.vscode-eslint", "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.addMissingImports": true, - "source.fixAll.eslint": true - } + "source.addMissingImports": "explicit", + "source.fixAll.eslint": "explicit" + }, + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#0894ba", + "activityBar.background": "#0894ba", + "activityBar.foreground": "#e7e7e7", + "activityBar.inactiveForeground": "#e7e7e799", + "activityBarBadge.background": "#99077a", + "activityBarBadge.foreground": "#e7e7e7", + "commandCenter.border": "#e7e7e799", + "sash.hoverBorder": "#0894ba", + "statusBar.background": "#066d89", + "statusBar.foreground": "#e7e7e7", + "statusBarItem.hoverBackground": "#0894ba", + "statusBarItem.remoteBackground": "#066d89", + "statusBarItem.remoteForeground": "#e7e7e7", + "titleBar.activeBackground": "#066d89", + "titleBar.activeForeground": "#e7e7e7", + "titleBar.inactiveBackground": "#066d8999", + "titleBar.inactiveForeground": "#e7e7e799" + }, + "peacock.color": "#066d89", + "cSpell.words": [ + "orbis", + "Orbis" + ] } diff --git a/config/default.json b/config/default.json index f594736..df624a9 100644 --- a/config/default.json +++ b/config/default.json @@ -1,12 +1,12 @@ { "constants": { "plataforma": "web", - "publicPath": "/smartoperation", + "publicPath": "/orbistemplate", "urlServerImages" : "https://gt.via-asesores.com/smartoperation/orbisapi/dtsrv/dev/operation?apikey=NTAzYzZlOTItMDcwZC00Zjg4LTljODMtNzBkNGQ5YjZhZTso", "urlWebApi": "https://gt.via-asesores.com/smartoperation/orbisapi/api/dev/operation?apikey=NTAzYzZlOTItMDcwZC00Zjg4LTljODMtNzBkNGQ5YjZhZTso", "urlUploadApi": "https://gt.via-asesores.com/smartoperation/orbisapi/upload/dev/operation?apikey=NTAzYzZlOTItMDcwZC00Zjg4LTljODMtNzBkNGQ5YjZhZTso", - "appTitle": "SmartOperation", - "idApp": "smartoperation", + "appTitle": "OrbisTemplate", + "idApp": "orbistemplate", "publicKey":"-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA205Ag8sXnqc0XsPa4NiS tZSca+3afzgkMdpotsIOphZxketyBLs4QOKYsAHGw51R68fbx5oLmDCn7a4n4ZtT u39ksIQg1lwQ3y7pqfb9BbYZKhtYigL8URUVrsQ5EuZxk9BOHHez59gizNzM+Vp0 zlnOuJVZdVdp3d+d1z+oE3ejsdXLGFEjAblo8GNQxTgxOXJk2VQ+4yQX5QN+mEYS FQpJqP9z5Y+/SVXlD3e943XjuNOFZwSG2uVkW3tuKsvGBOA38xLKydY9hb5y0WdM E0/hnOvB6gfIOovSmdTonDF3224iGQJa8RXss3SN+6NeLnhJQYGBri6U4sa0lNR/ 5vip/VCzaHliYERTztT2NgW6WUZAEW05gjN6Qid2eB7lKs/ND3BQkDHUKqouNDO1 xookeBqSg7fT/l3D6D7QzJE5Jc+bdZUDrr2MeYXehzbGg8sUBXJZbOu6GUkDSM5Y C8r/SnZhhA0ancQZZW/t4TmFNiLiGrqNS4uJf4UHKKsmXHCKDKB/bdlp60lTl6YF ocGzW6tBPdDFD7S5UTPqg//ob6mvuPFJ0E6t8Le60P+UiZIdmINe9dX9darS0VNH +eCVLj1J7iQNyXrelD5sE7xhAvQ3+jp3Q4mXWVgOZi1Uh/+/iNXDxrAtzKipYAOg zuyH0DDtO3E4JSiv4qr8o+UCAwEAAQ== -----END PUBLIC KEY-----", "maxMbUpload": 4, "defaultLocale": "es" diff --git a/config/gt.json b/config/gt.json index 895d8e8..7d5b701 100644 --- a/config/gt.json +++ b/config/gt.json @@ -1,12 +1,12 @@ { "constants": { "plataforma": "web", - "publicPath": "/smartoperation", + "publicPath": "/orbistemplate", "urlServerImages" : "https://gt.via-asesores.com/smartoperation/orbisapi/dtsrv/dev/orbis?apikey=NTAzYzZlOTItMDcwZC00Zjg4LTljODMtNzBkNGQ5YjZhZTMw", "urlWebApi": "https://gt.via-asesores.com/smartoperation/orbisapi/api/dev/orbis?apikey=NTAzYzZlOTItMDcwZC00Zjg4LTljODMtNzBkNGQ5YjZhZTMw", "urlUploadApi": "https://gt.via-asesores.com/smartoperation/orbisapi/upload/dev/orbis?apikey=NTAzYzZlOTItMDcwZC00Zjg4LTljODMtNzBkNGQ5YjZhZTMw", - "appTitle": "SmartOperation", - "idApp": "smartoperation", + "appTitle": "OrbisTemplate", + "idApp": "orbistemplate", "publicKey":"-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA205Ag8sXnqc0XsPa4NiS tZSca+3afzgkMdpotsIOphZxketyBLs4QOKYsAHGw51R68fbx5oLmDCn7a4n4ZtT u39ksIQg1lwQ3y7pqfb9BbYZKhtYigL8URUVrsQ5EuZxk9BOHHez59gizNzM+Vp0 zlnOuJVZdVdp3d+d1z+oE3ejsdXLGFEjAblo8GNQxTgxOXJk2VQ+4yQX5QN+mEYS FQpJqP9z5Y+/SVXlD3e943XjuNOFZwSG2uVkW3tuKsvGBOA38xLKydY9hb5y0WdM E0/hnOvB6gfIOovSmdTonDF3224iGQJa8RXss3SN+6NeLnhJQYGBri6U4sa0lNR/ 5vip/VCzaHliYERTztT2NgW6WUZAEW05gjN6Qid2eB7lKs/ND3BQkDHUKqouNDO1 xookeBqSg7fT/l3D6D7QzJE5Jc+bdZUDrr2MeYXehzbGg8sUBXJZbOu6GUkDSM5Y C8r/SnZhhA0ancQZZW/t4TmFNiLiGrqNS4uJf4UHKKsmXHCKDKB/bdlp60lTl6YF ocGzW6tBPdDFD7S5UTPqg//ob6mvuPFJ0E6t8Le60P+UiZIdmINe9dX9darS0VNH +eCVLj1J7iQNyXrelD5sE7xhAvQ3+jp3Q4mXWVgOZi1Uh/+/iNXDxrAtzKipYAOg zuyH0DDtO3E4JSiv4qr8o+UCAwEAAQ== -----END PUBLIC KEY-----", "maxMbUpload": 4, "defaultLocale": "es" diff --git a/config/testing.json b/config/testing.json index ee88561..dee3b1e 100644 --- a/config/testing.json +++ b/config/testing.json @@ -1,12 +1,12 @@ { "constants": { "plataforma": "web", - "publicPath": "/smartoperation", + "publicPath": "/orbistemplate", "urlServerImages" : "https://localhost:3000/smartoperation/orbisapi/dtsrv/dev/orbis?apikey=NTAzYzZlOTItMDcwZC00Zjg4LTljODMtNzBkNGQ5YjZhZTMw", "urlWebApi": "https://localhost:3000/smartoperation/orbisapi/api/dev/orbis?apikey=NTAzYzZlOTItMDcwZC00Zjg4LTljODMtNzBkNGQ5YjZhZTMw", "urlUploadApi": "https://localhost:3000/smartoperation/orbisapi/upload/dev/orbis?apikey=NTAzYzZlOTItMDcwZC00Zjg4LTljODMtNzBkNGQ5YjZhZTMw", - "appTitle": "SmartOperation", - "idApp": "smartoperation", + "appTitle": "OrbisTemplate", + "idApp": "orbistemplate", "publicKey":"-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA205Ag8sXnqc0XsPa4NiS tZSca+3afzgkMdpotsIOphZxketyBLs4QOKYsAHGw51R68fbx5oLmDCn7a4n4ZtT u39ksIQg1lwQ3y7pqfb9BbYZKhtYigL8URUVrsQ5EuZxk9BOHHez59gizNzM+Vp0 zlnOuJVZdVdp3d+d1z+oE3ejsdXLGFEjAblo8GNQxTgxOXJk2VQ+4yQX5QN+mEYS FQpJqP9z5Y+/SVXlD3e943XjuNOFZwSG2uVkW3tuKsvGBOA38xLKydY9hb5y0WdM E0/hnOvB6gfIOovSmdTonDF3224iGQJa8RXss3SN+6NeLnhJQYGBri6U4sa0lNR/ 5vip/VCzaHliYERTztT2NgW6WUZAEW05gjN6Qid2eB7lKs/ND3BQkDHUKqouNDO1 xookeBqSg7fT/l3D6D7QzJE5Jc+bdZUDrr2MeYXehzbGg8sUBXJZbOu6GUkDSM5Y C8r/SnZhhA0ancQZZW/t4TmFNiLiGrqNS4uJf4UHKKsmXHCKDKB/bdlp60lTl6YF ocGzW6tBPdDFD7S5UTPqg//ob6mvuPFJ0E6t8Le60P+UiZIdmINe9dX9darS0VNH +eCVLj1J7iQNyXrelD5sE7xhAvQ3+jp3Q4mXWVgOZi1Uh/+/iNXDxrAtzKipYAOg zuyH0DDtO3E4JSiv4qr8o+UCAwEAAQ== -----END PUBLIC KEY-----", "maxMbUpload": 4, "defaultLocale": "es" diff --git a/config/www.json b/config/www.json index 9a436e4..2f5ef4c 100644 --- a/config/www.json +++ b/config/www.json @@ -1,12 +1,12 @@ { "constants": { "plataforma": "web", - "publicPath": "/smartoperation", + "publicPath": "/orbistemplate", "urlServerImages" : "https://www.via-asesores.com/smartoperation/orbisapi/dtsrv/prod/orbis?apikey=MDJmNTEwOWUtNWY2ZC00OGJlLThjZGQtNWM4NmEyNmZmN2U5", "urlWebApi": "https://www.via-asesores.com/smartoperation/orbisapi/api/prod/orbis?apikey=MDJmNTEwOWUtNWY2ZC00OGJlLThjZGQtNWM4NmEyNmZmN2U5", "urlUploadApi": "https://www.via-asesores.com/smartoperation/orbisapi/upload/prod/orbis?apikey=MDJmNTEwOWUtNWY2ZC00OGJlLThjZGQtNWM4NmEyNmZmN2U5", - "appTitle": "SmartOperation", - "idApp": "smartoperation", + "appTitle": "OrbisTemplate", + "idApp": "orbistemplate", "publicKey":"-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA205Ag8sXnqc0XsPa4NiS tZSca+3afzgkMdpotsIOphZxketyBLs4QOKYsAHGw51R68fbx5oLmDCn7a4n4ZtT u39ksIQg1lwQ3y7pqfb9BbYZKhtYigL8URUVrsQ5EuZxk9BOHHez59gizNzM+Vp0 zlnOuJVZdVdp3d+d1z+oE3ejsdXLGFEjAblo8GNQxTgxOXJk2VQ+4yQX5QN+mEYS FQpJqP9z5Y+/SVXlD3e943XjuNOFZwSG2uVkW3tuKsvGBOA38xLKydY9hb5y0WdM E0/hnOvB6gfIOovSmdTonDF3224iGQJa8RXss3SN+6NeLnhJQYGBri6U4sa0lNR/ 5vip/VCzaHliYERTztT2NgW6WUZAEW05gjN6Qid2eB7lKs/ND3BQkDHUKqouNDO1 xookeBqSg7fT/l3D6D7QzJE5Jc+bdZUDrr2MeYXehzbGg8sUBXJZbOu6GUkDSM5Y C8r/SnZhhA0ancQZZW/t4TmFNiLiGrqNS4uJf4UHKKsmXHCKDKB/bdlp60lTl6YF ocGzW6tBPdDFD7S5UTPqg//ob6mvuPFJ0E6t8Le60P+UiZIdmINe9dX9darS0VNH +eCVLj1J7iQNyXrelD5sE7xhAvQ3+jp3Q4mXWVgOZi1Uh/+/iNXDxrAtzKipYAOg zuyH0DDtO3E4JSiv4qr8o+UCAwEAAQ== -----END PUBLIC KEY-----", "maxMbUpload": 4, "defaultLocale": "es" diff --git a/next.config.js b/next.config.js index 0350b76..e3b966d 100644 --- a/next.config.js +++ b/next.config.js @@ -12,17 +12,6 @@ const rewrites = async () => { ] } -const redirects = async () => { - return [ - { - source: '/mantenimientos', - destination: constants.publicPath, - basePath: true, - permanent: false - } - ] -} - const nextConfig = { env: { ...constants diff --git a/package.json b/package.json index a222ef8..02889fd 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,21 @@ "lint": "next lint" }, "dependencies": { + "@headlessui/react": "^1.7.17", + "config": "^3.3.9", + "js-cookie": "^3.0.5", + "js-md5": "^0.8.3", "next": "14.0.4", + "node-forge": "^1.3.1", + "pristinejs": "^1.1.0", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "react-dropzone-component": "^3.2.0", + "react-icons": "^4.12.0", + "react-simple-hook-store": "^0.0.6", + "react-toastify": "^9.1.3", + "rosetta": "^1.1.0", + "vComponents": "git+https://2bdcc0300e0ed5ac01f9dcd51368f7ac74fdb85a@git.via-asesores.com/libraries/v-components.git" }, "devDependencies": { "@types/node": "^20", diff --git a/src/components/Login/FormLogin.jsx b/src/components/Login/FormLogin.jsx new file mode 100644 index 0000000..c2e15a5 --- /dev/null +++ b/src/components/Login/FormLogin.jsx @@ -0,0 +1,141 @@ +import Pristine from 'pristinejs' +import { useEffect, useState } from 'react' +import { UserIcon, EyeIcon, EyeSlashIcon, KeyIcon } from '@heroicons/react/24/solid' +import functions from 'v-functions' +import useI18n from '@/hooks/useI18n' +import presets from '@/utils/globalPresets' +import useHasMounted from '@/hooks/useHasMounted' + +let validateLogin = null + +const FormLogin = ({ onLogin }) => { + const i18n = useI18n() + const hasMounted = useHasMounted() + const [viewPassword, setViewPassword] = useState(false) + const [isInputValid, setIsInputValid] = useState(false) + + const validate = (event) => { + if (event) { + event.preventDefault() + event.stopPropagation() + } + if (validateLogin && validateLogin !== null) { + const resulValidate = validateLogin.validate() + setIsInputValid(resulValidate) + } + } + + const doLogin = (event) => { + if (event) { + event.preventDefault() + event.stopPropagation() + } + if (isInputValid === true) { + const emailLogin = document.getElementById('usuario') + const passwordLogin = document.getElementById('clave') + const datos = { + email: functions.toUpperCaseAndTrim(emailLogin.value), + password: functions.trim(passwordLogin.value) + } + setIsInputValid(false) // para prevenir doble click + onLogin(datos) + } + } + + // when page is mounted + useEffect(() => { + if (hasMounted) { + const helem = document.getElementById('frmLogin') + validateLogin = new Pristine(helem, presets.pristine, false) + const helemUsuario = document.getElementById('usuario') + helemUsuario.focus() + } + }, [hasMounted]) + + useEffect(() => { + const handleKeyDown = (event) => { + if (event.key === 'Enter') { + validate(event) + if (isInputValid === true) { + doLogin(event) + event.preventDefault() + event.stopPropagation() + } + } + } + + document.addEventListener('keydown', handleKeyDown) + return () => { + document.removeEventListener('keydown', handleKeyDown) + } + }) + + return ( +
+ ) +} + +export default FormLogin diff --git a/src/components/SessionTimeout/index.jsx b/src/components/SessionTimeout/index.jsx new file mode 100644 index 0000000..666390e --- /dev/null +++ b/src/components/SessionTimeout/index.jsx @@ -0,0 +1,163 @@ +import React, { Fragment, useEffect, useRef, useState } from 'react' +import { Dialog, Transition } from '@headlessui/react' +import PropTypes from 'prop-types' +import useHasMounted from '@/hooks/useHasMounted' + +export default function SessionTimeout ({ i18n, user, onTimeout, onCancelTimeout }) { + const TIME = 30 + const hasMounted = useHasMounted() + const countTimerRef = useRef() + const [tick, setTick] = useState(0) + const [countTimer, setCountTimer] = useState() // contador que lleva el control de tiempo en segundos para mostrar antes de cerrar sesion + const [timerIsOn, setTimerIsOn] = useState(false) // verifica si el dialogo debe ser mostrado + const idleTimer = useRef(null) // variable de control para el proceso de conteo + + // realiza el conteo regresivo + const timedCount = () => { + if (countTimerRef.current <= 0) { + clearInterval(idleTimer.current) + setTimerIsOn(false) + setCountTimer(TIME) + onTimeout() + return + } + setCountTimer(countTimerRef.current - 1) + } + + // inicia el conteo regresivo + const startIdleMonitor = (expiracion = 0) => { + if (expiracion < TIME * 1000) { + countTimerRef.current = Math.round(expiracion / 1000) // tiempo restante + } else { + countTimerRef.current = TIME + } + setCountTimer(countTimerRef.current) + setTimerIsOn(true) + timedCount() + idleTimer.current = setInterval(timedCount, 1000) + } + + const calendarizeIdle = () => { + // const fecha_inicio_sesion = new Date(user.fecha_inicio_sesion.slice(0, -1)) // uso de localtime quita Z al final del string + const fecha_fin_sesion = new Date(user?.fecha_fin_sesion?.slice(0, -1)) // uso de localtime quita Z al final del string + // const fecha_fin_sesion = new Date(user.fecha_fin_sesion) + const expiracion = Math.round(fecha_fin_sesion.getTime() - (new Date()).getTime()) // valida cuando se realiza un F5 o reload del navegador (user.tiempo_expiracion * 60 * 1000) + if (expiracion <= 0) { + return // no debe calendarizar porque la sesion ya vencio + } + const timeInMillis = expiracion - (TIME * 1000) + const processTimer = setTimeout(startIdleMonitor, timeInMillis > 0 ? timeInMillis : 0, expiracion) + return () => clearTimeout(processTimer) + } + + // cancela el conteo regresivo aumentando el tiempo de expiracion + const stopIdleMonitor = async () => { + await onCancelTimeout() + clearInterval(idleTimer.current) + setTimerIsOn(false) + setCountTimer(TIME) + // force rerender to update view + setTick((tick) => tick + 1) + // await new Promise(resolve => setTimeout(resolve, 2000)) // espera obligada para actualizacion DB + } + + useEffect(() => { + if (timerIsOn === false && tick > 0) { + return calendarizeIdle() + } + }, [timerIsOn, tick]) + + useEffect(() => { + countTimerRef.current = countTimer + }, [countTimer]) + + useEffect(() => { + if (!user || user === null) { + if (idleTimer && idleTimer.current) { + clearInterval(idleTimer.current) + } + } + return () => clearInterval(idleTimer.current) + }, [user]) + + useEffect(() => { + if (hasMounted) { + return calendarizeIdle() + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hasMounted]) + + return ( + <> + { timerIsOn && timerIsOn === true && +
- Get started by editing
- src/pages/index.tsx
-
- Find in-depth information about Next.js features and API. -
- - - -- Learn about Next.js in an interactive course with quizzes! -
- - - -- Discover and deploy boilerplate example Next.js projects. -
- - - -- Instantly deploy your Next.js site to a shareable URL with Vercel. -
- -