From db28a17bf853f04633fe30d3bc6ee4f8e47bf219 Mon Sep 17 00:00:00 2001 From: Mario Arita Date: Tue, 26 Dec 2023 15:10:21 -0600 Subject: [PATCH] feat(initial): :tada: Initialize Next.js template --- .eslintrc.json | 49 +++- .gitignore | 9 +- .vscode/settings.json | 13 + LICENSE | 8 + config/default.json | 14 ++ config/gt.json | 14 ++ config/testing.json | 14 ++ config/www.json | 14 ++ next.config.js | 38 +++ package.json | 31 ++- public/images/cancel.png | Bin 0 -> 26674 bytes src/helper/clientApi.js | 103 ++++++++ src/hooks/useGlobalContainer.js | 9 + src/hooks/useGlobalFilters.js | 54 +++++ src/hooks/useHasMounted.js | 13 + src/hooks/useI18n.js | 9 + src/hooks/useLoading.js | 9 + src/hooks/useLocalStorage.js | 120 ++++++++++ src/hooks/useRoute.js | 76 ++++++ src/hooks/useScreenType.js | 35 +++ src/hooks/useStore.ts | 38 +++ src/layout/ResponsiveContainer.jsx | 272 +++++++++++++++++++++ src/lib/utils.js | 13 + src/pages/_document.tsx | 2 +- src/pages/api/hello.ts | 13 - src/pages/index.tsx | 28 +-- src/plugins/LoadingContext.jsx | 26 ++ src/plugins/i18nContext.jsx | 91 +++++++ src/utils/environment.js | 371 +++++++++++++++++++++++++++++ src/utils/globalPresets.js | 84 +++++++ 30 files changed, 1535 insertions(+), 35 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 LICENSE create mode 100644 config/default.json create mode 100644 config/gt.json create mode 100644 config/testing.json create mode 100644 config/www.json create mode 100644 public/images/cancel.png create mode 100644 src/helper/clientApi.js create mode 100644 src/hooks/useGlobalContainer.js create mode 100644 src/hooks/useGlobalFilters.js create mode 100644 src/hooks/useHasMounted.js create mode 100644 src/hooks/useI18n.js create mode 100644 src/hooks/useLoading.js create mode 100644 src/hooks/useLocalStorage.js create mode 100644 src/hooks/useRoute.js create mode 100644 src/hooks/useScreenType.js create mode 100644 src/hooks/useStore.ts create mode 100644 src/layout/ResponsiveContainer.jsx create mode 100644 src/lib/utils.js delete mode 100644 src/pages/api/hello.ts create mode 100644 src/plugins/LoadingContext.jsx create mode 100644 src/plugins/i18nContext.jsx create mode 100644 src/utils/environment.js create mode 100644 src/utils/globalPresets.js diff --git a/.eslintrc.json b/.eslintrc.json index bffb357..332ae12 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,50 @@ +// devDependencies required +// - eslint +// - eslint-config-next +// - eslint-config-standard +// - eslint-plugin-import +// - eslint-plugin-n +// - eslint-plugin-promise +// - eslint-plugin-react +// - eslint-plugin-tailwindcss + +// command yarn install + +// yarn add -D eslint eslint-plugin-react eslint-config-next eslint-config-standard eslint-plugin-tailwindcss eslint-plugin-n eslint-plugin-promise + { - "extends": "next/core-web-vitals" + "env": { + "browser": true, + "es2021": true, + "node": true + }, + "extends": [ + "plugin:react/recommended", + "next/core-web-vitals", + "standard", + "plugin:tailwindcss/recommended" + ], + "overrides": [], + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": ["react", "tailwindcss"], + "rules": { + "react-hooks/exhaustive-deps": "off", + "tailwindcss/no-custom-classname": "off", + "tailwindcss/classnames-order": "error", + "camelcase": "off", + "react-hooks/rules-of-hooks": "off", + "object-curly-spacing": [2, "always"], + "no-console": ["warn", { "allow": ["warn", "error"] }], + "indent": [ + "error", + 2, + { + "SwitchCase": 1, + "flatTernaryExpressions": true + } + ] + } } diff --git a/.gitignore b/.gitignore index fd3dbb5..3d085dc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ /node_modules /.pnp .pnp.js -.yarn/install-state.gz # testing /coverage @@ -24,6 +23,7 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +.pnpm-debug.log* # local env files .env*.local @@ -34,3 +34,10 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +.env*.local +yarn.lock +package-lock.json + +#yarn +/.yarn \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6f63eef --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "editor.tabSize": 2, + "editor.detectIndentation": false, + "search.exclude": { + "package-lock.json": true + }, + "editor.defaultFormatter": "dbaeumer.vscode-eslint", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.addMissingImports": true, + "source.fixAll.eslint": true + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b9c199c --- /dev/null +++ b/LICENSE @@ -0,0 +1,8 @@ +ISC License: + +Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") +Copyright (c) 1995-2003 by Internet Software Consortium + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/config/default.json b/config/default.json new file mode 100644 index 0000000..f594736 --- /dev/null +++ b/config/default.json @@ -0,0 +1,14 @@ +{ + "constants": { + "plataforma": "web", + "publicPath": "/smartoperation", + "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", + "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" + } +} \ No newline at end of file diff --git a/config/gt.json b/config/gt.json new file mode 100644 index 0000000..895d8e8 --- /dev/null +++ b/config/gt.json @@ -0,0 +1,14 @@ +{ + "constants": { + "plataforma": "web", + "publicPath": "/smartoperation", + "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", + "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" + } +} \ No newline at end of file diff --git a/config/testing.json b/config/testing.json new file mode 100644 index 0000000..ee88561 --- /dev/null +++ b/config/testing.json @@ -0,0 +1,14 @@ +{ + "constants": { + "plataforma": "web", + "publicPath": "/smartoperation", + "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", + "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" + } +} \ No newline at end of file diff --git a/config/www.json b/config/www.json new file mode 100644 index 0000000..9a436e4 --- /dev/null +++ b/config/www.json @@ -0,0 +1,14 @@ +{ + "constants": { + "plataforma": "web", + "publicPath": "/smartoperation", + "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", + "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" + } +} \ No newline at end of file diff --git a/next.config.js b/next.config.js index a843cbe..0350b76 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,44 @@ +const config = require('config') +const constants = config.get('constants') + /** @type {import('next').NextConfig} */ + +const rewrites = async () => { + return [ + { + source: '/orbisapi/:path*', + destination: 'http://localhost:9000/:path*' // Proxy to Backend + } + ] +} + +const redirects = async () => { + return [ + { + source: '/mantenimientos', + destination: constants.publicPath, + basePath: true, + permanent: false + } + ] +} + const nextConfig = { + env: { + ...constants + }, + images: { + domains: ['www.via-asesores.com', 'gt.via-asesores.com'], + unoptimized: true + }, + assetPrefix: constants.publicPath, + basePath: constants.publicPath, + compiler: { + styledComponents: true + }, + rewrites, reactStrictMode: true, + swcMinify: true } module.exports = nextConfig diff --git a/package.json b/package.json index f78d7f9..a222ef8 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,48 @@ { "name": "next-template", "version": "0.1.0", + "description": "My Next.js project", + "repository": { + "type": "git", + "url": "https://git.via-asesores.com/jmaritar/next-template" + }, + "keywords": [ + "operations", + "orbis", + "smart", + "route", + "sales", + "delivery" + ], + "author": "guatemala@via-asesores.com", + "license": "ISC", "private": true, "scripts": { "dev": "next dev", "build": "next build", + "generate": "next build && next export", "start": "next start", "lint": "next lint" }, "dependencies": { + "next": "14.0.4", "react": "^18", - "react-dom": "^18", - "next": "14.0.4" + "react-dom": "^18" }, "devDependencies": { - "typescript": "^5", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "autoprefixer": "^10.0.1", + "eslint": "^8.56.0", + "eslint-config-next": "^14.0.4", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-n": "^16.5.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-tailwindcss": "^3.13.0", "postcss": "^8", "tailwindcss": "^3.3.0", - "eslint": "^8", - "eslint-config-next": "14.0.4" + "typescript": "^5" } } diff --git a/public/images/cancel.png b/public/images/cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..82f2e648118855340ea44ce5ea6a2bdf7c164bba GIT binary patch literal 26674 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 Nu5Ws^OxGp){{Z { + // Send the form data to our API and get a response. + const response = await fetch(uri, { + // Body of the request is the JSON data we created above. + body: JSON.stringify(json), + // Tell the server we're sending JSON. + headers: { + 'Content-Type': 'application/json' + }, + // The method is POST because we are sending data. + method: 'POST' + }) + + // Get the response data from server as JSON. + // If server returns the name submitted, that means the form works. + const result = await response.json() + return result.data // return data rows +} + +const callUpload = (fileToUpload) => { + return new Promise(function (resolve, reject) { + // console.log('callUpload', constants.urlWebApi) + const params = { method: 'POST', body: fileToUpload } + // console.log('callUpload', params) + fetch(constants.urlUploadApi, params) + .then(response => response.json()) + .then((json) => { + // console.log('response upload', json) + resolve(json) + }).catch((error) => { + reject(error) + }) + }) +} + +const upload = async (fileUpload) => { + return await callUpload(fileUpload) +} + +export { upload } + +const execute = async (spName, params, encriptado = 0, loading = false, connInfo = undefined) => { + let model = {} + if (typeof spName === 'string') { + model.nombre = spName + model.loading = loading + // verifica que los parametros vengan como Array + model.parametros = params ?? [] + // si la información se envía encriptada + if (encriptado === 1) { + const paramsToEncrypt = { + nombre: spName, + parametros: params + } + const paramsEncryptedString = encryptRsa(JSON.stringify(paramsToEncrypt)) + + model.nombre = '' + model.encriptado = 1 + model.parametros = paramsEncryptedString + } + } else if (typeof spName === 'object') { + if (encriptado === 1) { + const paramsEncryptedString = encryptRsa(JSON.stringify(spName)) + model.parametros = paramsEncryptedString + model.encriptado = 1 + model.loading = loading + } else { + model = spName + model.loading = loading + } + } + + if (connInfo) { + return await callWs(connInfo.api + '/' + connInfo.env + '/' + connInfo.exposeRoute + '?apikey=' + connInfo.apikey, model) + } + return await callWs(constants.urlWebApi + uris.procedure, model) +} + +export { execute } diff --git a/src/hooks/useGlobalContainer.js b/src/hooks/useGlobalContainer.js new file mode 100644 index 0000000..69d6835 --- /dev/null +++ b/src/hooks/useGlobalContainer.js @@ -0,0 +1,9 @@ +import { useContext } from 'react' +import { LayoutContext } from '@/layout/ResponsiveContainer' + +const useGlobalContainer = () => { + const container = useContext(LayoutContext) + return container +} + +export default useGlobalContainer diff --git a/src/hooks/useGlobalFilters.js b/src/hooks/useGlobalFilters.js new file mode 100644 index 0000000..44d0adf --- /dev/null +++ b/src/hooks/useGlobalFilters.js @@ -0,0 +1,54 @@ +import { useState, useEffect } from 'react' +import functions from 'v-functions' + +const safeGetPathname = () => { + return typeof window !== 'undefined' ? window.location.pathname : '' +} + +const useGlobalFilters = () => { + const [rutaActual, setRutaActual] = useState(safeGetPathname()) + + useEffect(() => { + setRutaActual(safeGetPathname()) + }, [safeGetPathname()]) + + const getFiltersFromLocalStorage = () => { + const storedFilters = functions.getDecodeStorage('globalFilters') + const parsedFilters = storedFilters ? JSON.parse(storedFilters) : {} + return parsedFilters[rutaActual] || {} + } + + const setFilters = (keyOrObject, value) => { + const prevFilters = getFiltersFromLocalStorage() + let updatedFilters + + if (typeof keyOrObject === 'object' && value === undefined) { + updatedFilters = { ...prevFilters, ...keyOrObject } + } else { + updatedFilters = { ...prevFilters, [keyOrObject]: value } + } + + const storedFilters = functions.getDecodeStorage('globalFilters') + const globalFilters = storedFilters ? JSON.parse(storedFilters) : {} + functions.setEncodeStorage( + 'globalFilters', + JSON.stringify({ ...globalFilters, [rutaActual]: updatedFilters }) + ) + } + + const clearFiltersForRoute = () => { + const storedFilters = functions.getDecodeStorage('globalFilters') + const globalFilters = storedFilters ? JSON.parse(storedFilters) : {} + functions.setEncodeStorage('globalFilters', JSON.stringify({ ...globalFilters, [rutaActual]: {} })) + } + + const clearAllFilters = () => { + functions.setEncodeStorage('globalFilters', JSON.stringify({})) + } + + const filters = getFiltersFromLocalStorage() + + return [filters, setFilters, clearFiltersForRoute, clearAllFilters] +} + +export default useGlobalFilters diff --git a/src/hooks/useHasMounted.js b/src/hooks/useHasMounted.js new file mode 100644 index 0000000..61491c4 --- /dev/null +++ b/src/hooks/useHasMounted.js @@ -0,0 +1,13 @@ +import { useEffect, useState } from 'react' + +const useHasMounted = () => { + const [hasMounted, setHasMounted] = useState(false) + + useEffect(() => { + setHasMounted(true) + }, []) + + return hasMounted +} + +export default useHasMounted diff --git a/src/hooks/useI18n.js b/src/hooks/useI18n.js new file mode 100644 index 0000000..cc7ae31 --- /dev/null +++ b/src/hooks/useI18n.js @@ -0,0 +1,9 @@ +import { useContext } from 'react' +import { I18nContext } from '@/plugins/i18nContext' + +const useI18n = () => { + const i18n = useContext(I18nContext) + return i18n +} + +export default useI18n diff --git a/src/hooks/useLoading.js b/src/hooks/useLoading.js new file mode 100644 index 0000000..d9d1863 --- /dev/null +++ b/src/hooks/useLoading.js @@ -0,0 +1,9 @@ +import { useContext } from 'react' +import { LoadingContext } from '@/plugins/LoadingContext' + +const useLoading = () => { + const loading = useContext(LoadingContext) + return loading +} + +export default useLoading diff --git a/src/hooks/useLocalStorage.js b/src/hooks/useLocalStorage.js new file mode 100644 index 0000000..5fa2f7c --- /dev/null +++ b/src/hooks/useLocalStorage.js @@ -0,0 +1,120 @@ +import { useState, useEffect, useCallback } from 'react' +import PropTypes from 'prop-types' +import functions from 'v-functions' + +/** + * Función para parsear JSON. Maneja los valores 'undefined' y errores de parseo. + * @param {string | null} value - El valor JSON como string para parsear. + * @returns {any} El objeto JavaScript parseado o undefined si hay un error. + */ +function parseJSON (value) { + try { + return value === 'undefined' ? undefined : JSON.parse(value ?? '') + } catch { + console.error('Error al parsear', { value }) + return undefined + } +} + +/** + * Hook personalizado para interactuar con el localStorage del navegador. + * Permite almacenar, actualizar y recuperar un valor de localStorage. + * + * @param {string} key - La clave bajo la cual almacenar el valor en localStorage. + * @param {any} initialValue - El valor inicial a usar si no hay nada en localStorage. + * @returns {Array} Un array con el valor almacenado y la función para actualizarlo. + * + * @example + * + * // Uso en un componente React funcional + * const MyComponent = () => { + * // Utiliza el hook para almacenar un valor en localStorage + * const [name, setName] = useLocalStorage('name', 'Nombre inicial'); + * + * return ( + *
+ * setName(e.target.value)} + * /> + *

El nombre es: {name}

+ *
+ * ); + * } + */ +const useLocalStorage = (key, initialValue) => { + const [storedValue, setStoredValue] = useState(initialValue) + const [initialized, setInitialized] = useState(false) + + const readValue = useCallback(() => { + if (typeof window === 'undefined') { + return initialValue + } + + try { + // const item = window.localStorage.getItem(key) + const item = functions.getDecodeStorage(key) + return item ? parseJSON(item) : initialValue + } catch (error) { + console.warn(`Error al leer la clave ${key} del localStorage:`, error) + return initialValue + } + }, [key, initialValue]) + + useEffect(() => { + setInitialized(true) + setStoredValue(readValue()) + }, [readValue]) + + useEffect(() => { + if (!initialized) { + return + } + + const handleStorageChange = (event) => { + if (event.key && event.key !== key) { + return + } + setStoredValue(readValue()) + } + + window.addEventListener('storage', handleStorageChange) + window.addEventListener('local-storage', handleStorageChange) + + return () => { + window.removeEventListener('storage', handleStorageChange) + window.removeEventListener('local-storage', handleStorageChange) + } + }, [key, readValue, initialized]) + + const setValue = (value) => { + if (typeof window === 'undefined' || !initialized) { + console.warn(`Intentando establecer la clave ${key} del localStorage en un entorno no cliente.`) + return + } + + try { + const newValue = value instanceof Function ? value(storedValue) : value + // window.localStorage.setItem(key, JSON.stringify(newValue)) + functions.setEncodeStorage(key, JSON.stringify(newValue)) + setStoredValue(newValue) + window.dispatchEvent(new Event('local-storage')) + } catch (error) { + console.warn(`Error al establecer la clave ${key} en el localStorage:`, error) + } + } + + return [storedValue, setValue] +} + +useLocalStorage.propTypes = { + key: PropTypes.string.isRequired, + initialValue: PropTypes.any +} + +useLocalStorage.defaultProps = { + initialValue: null +} + +export default useLocalStorage diff --git a/src/hooks/useRoute.js b/src/hooks/useRoute.js new file mode 100644 index 0000000..0e620e9 --- /dev/null +++ b/src/hooks/useRoute.js @@ -0,0 +1,76 @@ +import { createContext, useContext, useState, useEffect } from 'react' +import environment from '@/utils/environment' + +export const RouteContext = createContext() + +export const useRoute = () => { + return useContext(RouteContext) +} + +export const RouteProvider = ({ children }) => { + const [breadcrumbsList, setBreadcrumbsList] = useState([]) + + useEffect(() => { + const storedBreadcrumbs = JSON.parse(sessionStorage.getItem('breadcrumbsList')) || [] + if (storedBreadcrumbs.length !== 0) { + setBreadcrumbsList(storedBreadcrumbs) + } + }, []) + + useEffect(() => { + sessionStorage.setItem('breadcrumbsList', JSON.stringify(breadcrumbsList)) + }, [breadcrumbsList]) + + const addBreadcrumb = (title, location) => { + const timestamp = '/' + environment.getTime() + if (location.endsWith(timestamp)) { + location = location.replace(timestamp, '') + } else if (location.endsWith('[...index]')) { + location = location.replace('/[...index]', '') + } + + setBreadcrumbsList(prev => { + const lastBreadcrumb = prev[prev.length - 1] + if (lastBreadcrumb && lastBreadcrumb.location === location && lastBreadcrumb.title === title) { + return prev + } + return [...prev, { title, location }] + }) + } + + const breadcrumbsListArr = () => { + return breadcrumbsList.slice(0, breadcrumbsList.length - 1) + } + + const truncateBreadcrumbs = (index) => { + setBreadcrumbsList(prev => prev.slice(0, index)) + } + + useEffect(() => { + const handlePopState = () => { + setBreadcrumbsList(prev => { + const newArr = [...prev] + newArr.pop() + return newArr + }) + } + + window.addEventListener('popstate', handlePopState) + return () => { + window.removeEventListener('popstate', handlePopState) + } + }, []) + + const value = { + breadcrumbsListArr, + addBreadcrumb, + truncateBreadcrumbs, + breadcrumbsList // Incluir breadcrumbsList en el valor del contexto + } + + return ( + + {children} + + ) +} diff --git a/src/hooks/useScreenType.js b/src/hooks/useScreenType.js new file mode 100644 index 0000000..c2696ba --- /dev/null +++ b/src/hooks/useScreenType.js @@ -0,0 +1,35 @@ +import { useEffect, useState } from 'react' +import mq from 'js-mq' + +const useScreenType = () => { + const [isMobile, setIsMobile] = useState(null) + + useEffect(() => { + if (typeof window !== 'undefined' && typeof document !== 'undefined') { + try { + mq.register([ + { name: 'mobile', query: '(max-width: 767px)' }, + { name: 'desktop', query: '(min-width: 768px)' } + ]) + mq.on('mobile', (e) => { + setIsMobile(true) + }) + mq.on('desktop', (e) => { + setIsMobile(false) + }) + const arrayEstadoMq = mq.getState() + if (arrayEstadoMq.length && (arrayEstadoMq[0] === 'not-mobile' || arrayEstadoMq[0] === 'desktop')) { + setIsMobile(false) + } else { + setIsMobile(true) + } + } catch (e) { + console.error(`Error al registrar mq breackpoints - ${e.message}`) + } + } + }, []) + + return isMobile +} + +export default useScreenType diff --git a/src/hooks/useStore.ts b/src/hooks/useStore.ts new file mode 100644 index 0000000..19fea1d --- /dev/null +++ b/src/hooks/useStore.ts @@ -0,0 +1,38 @@ +import { createStore } from 'react-simple-hook-store' + +interface IState { + title: string; + usuarioPermisos: object | null; + valueEasyMDE: string; +} + +interface IActions { + setTitle: (newState: string) => void; + setUsuarioPermisos: (newState: object | null) => void; + setValueForEasyMDE: (newState: string) => void; +} + +export const { useStore, store } = createStore( + { + title: '', + usuarioPermisos: null, + valueEasyMDE: null + }, + { + setTitle: (store, newState) => { + store.setState({ + title: newState + }) + }, + setUsuarioPermisos: (store, newState) => { + store.setState({ + usuarioPermisos: newState + }) + }, + setValueForEasyMDE: (store, newState) => { + store.setState({ + valueEasyMDE: newState + }) + } + } +) diff --git a/src/layout/ResponsiveContainer.jsx b/src/layout/ResponsiveContainer.jsx new file mode 100644 index 0000000..45440a2 --- /dev/null +++ b/src/layout/ResponsiveContainer.jsx @@ -0,0 +1,272 @@ +import { useEffect, useState, createContext, useMemo } from 'react' +import { useRouter } from 'next/router' +import { execute } from '@/helper/clientApi' +import environment from '@/utils/environment' +import presets from '@/utils/globalPresets' +import dynamic from 'next/dynamic' +import useLoading from '@/hooks/useLoading' +import { useStore } from '@/hooks/useStore' +import useHasMounted from '@/hooks/useHasMounted' +import useI18n from '@/hooks/useI18n' +import SessionTimeout from '@/components/SessionTimeout' +import UserOptionsMenu from '@/components/widgets/UserOptionsMenu' +import { toast } from 'react-toastify' + +export const LayoutContext = createContext() + +const Footer = dynamic(() => { return import('vComponents/dist/Footer') }, { ssr: false }) +const Sidebar = dynamic(() => { return import('vComponents/dist/Sidebar') }, { ssr: false }) +const Navbar = dynamic(() => { return import('vComponents/dist/Navbar') }, { ssr: false }) + +const ResponsiveContainer = ({ children }) => { + const router = useRouter() + const loading = useLoading() + const hasMounted = useHasMounted() + const i18n = useI18n() + const [_i18n, setI18n] = useState({ t: () => { return 'txtNone' } }) + + const [sidebarOpen, setSidebarOpen] = useState(true) + const [menu, setMenu] = useState([]) + const [title, setTitle] = useStore(s => s.title, a => a.setTitle) + const [userObj, setUserObj] = useState() + const [token, setToken] = useState('') + const [theme, setTheme] = useState(null) + const [constanteObj, setConstanteObj] = useState('') + + const doLogout = async () => { + const redirectPath = await environment.logout() + await i18n.locale('es') + setUserObj(null) + setConstanteObj(null) + setTheme(null) + router.push(redirectPath) + } + + // se ejecuta cuando expira la sesion + const onTimeout = () => { + doLogout() + } + + // se ejecuta cuando el usuario indica explicitamente que sigue conectado en la sesion + const onCancelTimeout = async () => { + const sprUpdateSesion = 'SPR_SESION_U' + const result = await execute(sprUpdateSesion, [token]) + if (environment.validaResultadoDB(result) === true) { + const updateUserObj = { ...userObj } + updateUserObj.tiempo_expiracion = result[0].tiempo_expiracion + updateUserObj.fecha_fin_sesion = result[0].fecha_fin_sesion + setUserObj(updateUserObj) + await environment.login(token, updateUserObj) + } + } + + const setPreferences = async (opcion) => { + const env = await environment.getEnvUser() + // console.log((new Date()).toString(),'setPreferences userObj', userObj) + const sprInfoSesion = 'SPR_INFO_USUARIOS' + const response3 = await execute(sprInfoSesion, [env.token, opcion]) + if (environment.validaResultadoDB(response3, i18n, toast, false) === true) { + // console.log('setPreferences', response3) + if (opcion === 'en' || opcion === 'es') { + env.user.i18n = opcion + // setLanguage(opcion) + } + if (opcion === 'light' || opcion === 'dark') { + env.user.theme = opcion + setTheme(opcion) + } + environment.setEnvUser(env.user) + if (environment.validaResultadoDB(response3, i18n, toast, false) === true) { + if (response3 && response3[0]) { + toast.success(response3[0].response, presets.toaster) + } + } + } + } + + const onBackKeyDown = () => { + if (router.asPath === '/') { + // cuando esta en la pagina principal cierra la app + if (navigator.app) { + navigator.app.exitApp() + } else if (navigator.device) { + navigator.device.exitApp() + } else { + window.close() + } + } else { + window.history.back() + } + } + + const validaPermisosRuta = async (token) => { + if (router.pathname.includes('/admin')) { + loading.start() + const tienePermiso = await environment.validaPermisos(token, 'S', 'N', 'N', 'N') + loading.stop() + if (tienePermiso === false) { + router.push(presets.locations.welcome) + } + } + if (router.pathname.includes('/mantenimientos')) { + loading.start() + const tienePermiso = await environment.validaPermisos(token, 'N', 'S', 'N', 'N') + loading.stop() + if (tienePermiso === false) { + router.push(presets.locations.welcome) + } + } + } + + const getMenu = async () => { + loading.start() + setMenu([]) + if (token && token !== null) { + const options = await execute('SPR_MENU_S', [token, 'BO', null]) + setMenu(options) + } + loading.stop() + } + + const getEnv = async () => { + try { + const env = await environment.getEnvUser() + + if (!env || !env.token) { + doLogout() + return + } + setUserObj(env.user) + setConstanteObj(env.constante) + setToken(env.token) + setTheme(env.user.theme || 'light') + if (!env.user.i18n) { + const browserLanguage = window.navigator.language + if (browserLanguage === 'en' || browserLanguage === 'en-US') { + setPreferences('en') + } else { + setPreferences('es') + } + } else { + setPreferences(env.user.idioma) + } + } catch (error) { + doLogout() + } + } + + const getTheme = () => { + if (!theme || theme != null) { + return theme + } + return 'light' + } + + const containerWrapper = useMemo(() => ({ + userObj, + setUserObj, + constanteObj, + setConstanteObj, + token, + setToken, + setTheme, + setTitle + }), [userObj, setUserObj, constanteObj, setConstanteObj, token, setToken, setTheme, setTitle]) + + useEffect(() => { + setI18n(i18n) + }, [i18n]) + + useEffect(() => { + getMenu() + }, [token]) + + // when page is mounted + useEffect(() => { + if (hasMounted) { + getEnv().then(() => { + getMenu() + }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hasMounted]) + + useEffect(() => { + if (process.browser && window.cordova) { + document.addEventListener('backbutton', onBackKeyDown, false) + } + }) + + return ( + + +
+ {/* Sidebar */} + { + userObj && userObj.nombre_usuario && + + doLogout()} + setTitle={setTitle} + userObj={userObj} + environment={environment} + presets={presets} + router={router} + i18n={_i18n} + /> + } + + {/* Content area */} +
+ + {/* Site header */} + {userObj && userObj.nombre_usuario && + + doLogout()} + onClickProfile={() => router.push(`${presets.locations.profile}/${environment.getTime()}`)} + MenuOptions={() => } + title={title} + userObj={userObj} + router={router} + presets={presets} + + /> + + } + +
+
+ {children} +
+
+ { + userObj && userObj.nombre_usuario && + onTimeout()} onCancelTimeout={() => onCancelTimeout()} /> + } +
+ +
+