Compare commits
5 Commits
54d2c520e5
...
27df47803c
Author | SHA1 | Date | |
---|---|---|---|
27df47803c | |||
10e935d2e7 | |||
956c5518f6 | |||
36757bd163 | |||
db28a17bf8 |
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -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
|
37
.vscode/settings.json
vendored
Normal file
37
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.detectIndentation": false,
|
||||
"search.exclude": {
|
||||
"package-lock.json": true
|
||||
},
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.addMissingImports": "explicit",
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#fac8fb",
|
||||
"activityBar.background": "#fac8fb",
|
||||
"activityBar.foreground": "#15202b",
|
||||
"activityBar.inactiveForeground": "#15202b99",
|
||||
"activityBarBadge.background": "#95920b",
|
||||
"activityBarBadge.foreground": "#15202b",
|
||||
"commandCenter.border": "#15202b99",
|
||||
"sash.hoverBorder": "#fac8fb",
|
||||
"statusBar.background": "#f598f8",
|
||||
"statusBar.foreground": "#15202b",
|
||||
"statusBarItem.hoverBackground": "#f068f5",
|
||||
"statusBarItem.remoteBackground": "#f598f8",
|
||||
"statusBarItem.remoteForeground": "#15202b",
|
||||
"titleBar.activeBackground": "#f598f8",
|
||||
"titleBar.activeForeground": "#15202b",
|
||||
"titleBar.inactiveBackground": "#f598f899",
|
||||
"titleBar.inactiveForeground": "#15202b99"
|
||||
},
|
||||
"peacock.color": "#f598f8",
|
||||
"cSpell.words": [
|
||||
"orbis",
|
||||
"Orbis"
|
||||
]
|
||||
}
|
39
Dockerfile
Normal file
39
Dockerfile
Normal file
@ -0,0 +1,39 @@
|
||||
## Build image and name it 'custom-next'
|
||||
# docker build -t custom-next .
|
||||
|
||||
## Run container and name it 'OrbisPlayground'. Webpage is localhost:3000
|
||||
# docker run -it --rm -dp 3000:3000 --name OrbisPlayground custom-next
|
||||
|
||||
## Connect to container
|
||||
# docker exec -it OrbisPlayground sh
|
||||
|
||||
## Stop docker container
|
||||
# docker stop OrbisPlayground
|
||||
|
||||
## All together
|
||||
# docker stop OrbisPlayground & docker image rm -f custom-next & docker build -t custom-next . && docker run -it --rm -dp 3000:3000 --name OrbisPlayground custom-next && docker exec -it OrbisPlayground sh
|
||||
|
||||
# Start Dockerfile
|
||||
ARG VERSION=18.16.0-alpine3.17
|
||||
ARG DIR=OrbisPlayground
|
||||
|
||||
FROM node:${VERSION} as builder
|
||||
# redeclare ARG because ARG not in build environment
|
||||
ARG DIR
|
||||
WORKDIR /${DIR}
|
||||
COPY . .
|
||||
RUN apk update
|
||||
RUN apk add git
|
||||
RUN yarn
|
||||
RUN NODE_ENV=production yarn build
|
||||
|
||||
FROM node:${VERSION} as runner
|
||||
# redeclare ARG because ARG not in build environment
|
||||
ARG DIR
|
||||
WORKDIR /${DIR}
|
||||
COPY --from=builder /${DIR}/public ./public
|
||||
COPY --from=builder /${DIR}/.next/standalone .
|
||||
COPY --from=builder /${DIR}/.next/static ./.next/static
|
||||
|
||||
EXPOSE 3000
|
||||
ENTRYPOINT ["node", "server.js"]
|
8
LICENSE
Normal file
8
LICENSE
Normal file
@ -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.
|
14
config/default.json
Normal file
14
config/default.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"constants": {
|
||||
"plataforma": "web",
|
||||
"publicPath": "/orbisPlayground",
|
||||
"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": "orbisPlayground",
|
||||
"idApp": "orbisPlayground",
|
||||
"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"
|
||||
}
|
||||
}
|
14
config/gt.json
Normal file
14
config/gt.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"constants": {
|
||||
"plataforma": "web",
|
||||
"publicPath": "/orbisPlayground",
|
||||
"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": "orbisPlayground",
|
||||
"idApp": "orbisPlayground",
|
||||
"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"
|
||||
}
|
||||
}
|
14
config/testing.json
Normal file
14
config/testing.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"constants": {
|
||||
"plataforma": "web",
|
||||
"publicPath": "/orbisPlayground",
|
||||
"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": "orbisPlayground",
|
||||
"idApp": "orbisPlayground",
|
||||
"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"
|
||||
}
|
||||
}
|
14
config/www.json
Normal file
14
config/www.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"constants": {
|
||||
"plataforma": "web",
|
||||
"publicPath": "/orbisPlayground",
|
||||
"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": "orbisPlayground",
|
||||
"idApp": "orbisPlayground",
|
||||
"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"
|
||||
}
|
||||
}
|
10
jsconfig.json
Normal file
10
jsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"vComponents/*": ["./node_modules/vComponents/dist/components/*"],
|
||||
"via-ui/*": ["./node_modules/via-ui/dist/components/*"]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,33 @@
|
||||
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 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
|
||||
|
54
package.json
54
package.json
@ -1,27 +1,71 @@
|
||||
{
|
||||
"name": "next-template",
|
||||
"name": "orbis-playground",
|
||||
"version": "0.1.0",
|
||||
"description": "Orbis Playground",
|
||||
"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": {
|
||||
"@babel/standalone": "^7.23.7",
|
||||
"@codemirror/lang-javascript": "^6.2.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@codesandbox/sandpack-react": "^2.10.0",
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@uiw/codemirror-theme-monokai": "^4.21.21",
|
||||
"@uiw/react-codemirror": "^4.21.21",
|
||||
"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",
|
||||
"next": "14.0.4"
|
||||
"react-dropzone-component": "^3.2.0",
|
||||
"react-icons": "^4.12.0",
|
||||
"react-live": "^4.1.5",
|
||||
"react-live-runner": "^1.0.5",
|
||||
"react-runner": "^1.0.3",
|
||||
"react-simple-hook-store": "^0.0.6",
|
||||
"react-toastify": "^9.1.3",
|
||||
"rosetta": "^1.1.0",
|
||||
"v-functions": "git+https://2bdcc0300e0ed5ac01f9dcd51368f7ac74fdb85a@git.via-asesores.com/libraries/v-functions.git",
|
||||
"vComponents": "git+https://2bdcc0300e0ed5ac01f9dcd51368f7ac74fdb85a@git.via-asesores.com/libraries/v-components.git#dev_ja",
|
||||
"via-ui": "git+https://2bdcc0300e0ed5ac01f9dcd51368f7ac74fdb85a@git.via-asesores.com/libraries/via-ui.git#dev_ja"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
|
BIN
public/images/backgrounds.webp
Normal file
BIN
public/images/backgrounds.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
public/images/cancel.png
Normal file
BIN
public/images/cancel.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
32
src/components/CodeEditor/index.jsx
Normal file
32
src/components/CodeEditor/index.jsx
Normal file
@ -0,0 +1,32 @@
|
||||
import React, { useState } from 'react'
|
||||
import CodeMirror from '@uiw/react-codemirror'
|
||||
import { javascript } from '@codemirror/lang-javascript'
|
||||
import { monokai } from '@uiw/codemirror-theme-monokai'
|
||||
import { useStore } from '@/hooks/useStore'
|
||||
// import { oneDark } from '@codemirror/theme-one-dark'
|
||||
|
||||
const CodeEditor = ({ code, onChange }) => {
|
||||
const [currentCode, setCurrentCode] = useState(code)
|
||||
|
||||
const [theme] = useStore(s => s.theme, a => a.setTheme)
|
||||
|
||||
const onCodeChange = (value, viewUpdate) => {
|
||||
onChange(value)
|
||||
setCurrentCode(value)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<CodeMirror
|
||||
value={currentCode}
|
||||
extensions={[javascript({ jsx: true })]}
|
||||
theme={theme === 'dark' ? monokai : undefined}
|
||||
className='shadow-neon max-h-[50vh] overflow-auto rounded-lg border'
|
||||
// readOnly={true}
|
||||
onChange={onCodeChange}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default CodeEditor
|
88
src/components/CodePlayground/index.jsx
Normal file
88
src/components/CodePlayground/index.jsx
Normal file
@ -0,0 +1,88 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Icons } from '../Icons'
|
||||
import { LiveProvider, LiveError, LivePreview } from 'react-live'
|
||||
import CodeEditor from '../CodeEditor'
|
||||
import useKeyPress from '@/hooks/useKeyPress'
|
||||
import { toast } from 'react-toastify'
|
||||
import presets from '@/utils/globalPresets'
|
||||
|
||||
const CodePlayground = ({ code, onChange, scope }) => {
|
||||
const [view, setView] = useState('preview')
|
||||
const [currentCode, setCurrentCode] = useState(code)
|
||||
|
||||
const toggleView = () => {
|
||||
setView(view === 'preview' ? 'code' : 'preview')
|
||||
}
|
||||
|
||||
useKeyPress('Tab', toggleView, true)
|
||||
|
||||
const copyToClipboard = () => {
|
||||
navigator.clipboard.writeText(code).then(() => {
|
||||
toast.success('Código copiado al portapapeles', presets.toaster)
|
||||
}).catch(err => {
|
||||
console.error('Algo salió mal al copiar el código: ', err)
|
||||
})
|
||||
}
|
||||
|
||||
const handleCodeChange = (editor, data, value) => {
|
||||
setCurrentCode(editor)
|
||||
onChange(editor)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentCode(code)
|
||||
}, [code])
|
||||
|
||||
return (
|
||||
<div className='flex-row'>
|
||||
<div className='mb-3 flex justify-end'>
|
||||
<div className="shadow-neon shadow-neon-sky flex space-x-1 rounded-lg bg-slate-200 dark:!bg-slate-900">
|
||||
<button
|
||||
onClick={() => setView('preview')}
|
||||
className={`flex items-center rounded-md px-2 py-[0.4375rem] pr-3 text-sm font-semibold ${view === 'preview' ? 'bg-white shadow dark:!bg-[#333333]' : 'bg-slate-200 dark:!bg-transparent'}`}
|
||||
>
|
||||
<Icons.Eye className={`h-5 w-5 flex-none ${view === 'preview' ? 'stroke-sky-500 dark:stroke-sky-300' : 'stroke-slate-600 dark:stroke-slate-400'}`}/>
|
||||
<span className={`ml-2 ${view === 'preview' ? 'text-sky-500 dark:text-sky-300' : 'text-slate-600 dark:text-slate-400'}`}>
|
||||
Preview
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setView('code')}
|
||||
className={`flex items-center rounded-md px-2 py-[0.4375rem] pr-3 text-sm font-semibold ${view === 'code' ? 'bg-white shadow dark:!bg-[#333333]' : 'bg-slate-200 dark:!bg-transparent'}`}
|
||||
>
|
||||
<Icons.Code className={`h-5 w-5 flex-none ${view === 'code' ? 'stroke-sky-500 dark:stroke-sky-300' : 'stroke-slate-600 dark:stroke-slate-400'}`}/>
|
||||
<span className={`ml-2 ${view === 'code' ? 'text-sky-500 dark:text-sky-300' : 'text-slate-600 dark:text-slate-400'}`}>
|
||||
Code
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='my-1 ml-6 mr-3 h-auto w-[2px] bg-slate-900/10 dark:bg-slate-900/20' />
|
||||
|
||||
<button
|
||||
onClick={copyToClipboard}
|
||||
className="shadow-neon shadow-neon-sky group relative flex h-9 w-9 items-center justify-center rounded-md bg-white text-slate-600 hover:text-slate-900 dark:!bg-[#333333] dark:!stroke-sky-300 dark:hover:!text-sky-100"
|
||||
>
|
||||
<Icons.Copy className="h-5 w-5 stroke-slate-400 transition group-hover:rotate-[-4deg] group-hover:stroke-slate-600 group-active:group-hover:stroke-sky-500 dark:group-hover:stroke-sky-300" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<LiveProvider code={currentCode} scope={scope} noInline={true}>
|
||||
{ view === 'preview' &&
|
||||
<div className={`${view === 'preview' ? '' : 'hidden'}`}>
|
||||
<LivePreview className='shadow-neon rounded-lg border p-2' />
|
||||
<LiveError className="mt-2 rounded bg-red-500 text-white" />
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className={`${view === 'code' ? '' : 'hidden'}`}>
|
||||
<CodeEditor code={currentCode} onChange={handleCodeChange} />
|
||||
</div>
|
||||
</LiveProvider>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CodePlayground
|
25
src/components/Icons/index.jsx
Normal file
25
src/components/Icons/index.jsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { BsSearch } from 'react-icons/bs'
|
||||
import { IoEllipsisVertical, IoFilter } from 'react-icons/io5'
|
||||
import { DocumentPlusIcon, EyeIcon, PencilSquareIcon } from '@heroicons/react/24/solid'
|
||||
import { FaChevronLeft, FaChevronRight, FaPaperclip, FaUser } from 'react-icons/fa'
|
||||
import { RiEyeLine } from 'react-icons/ri'
|
||||
import { LuEye, LuCode2, LuClipboardList } from 'react-icons/lu'
|
||||
|
||||
export const Icons = {
|
||||
// Common Icons
|
||||
Search: BsSearch,
|
||||
New: DocumentPlusIcon,
|
||||
Edit: PencilSquareIcon,
|
||||
View: IoFilter,
|
||||
Pre: FaChevronLeft,
|
||||
Next: FaChevronRight,
|
||||
User: FaUser,
|
||||
Details: EyeIcon,
|
||||
Attachments: FaPaperclip,
|
||||
Options: IoEllipsisVertical,
|
||||
|
||||
Eye: LuEye,
|
||||
Code: LuCode2,
|
||||
Copy: LuClipboardList
|
||||
|
||||
}
|
41
src/components/LivePreview/index.jsx
Normal file
41
src/components/LivePreview/index.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { transform } from '@babel/standalone'
|
||||
|
||||
const LivePreview = ({ code }) => {
|
||||
const iframeRef = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!iframeRef.current) return
|
||||
|
||||
// Aquí, asegúrate de que 'code' contenga solo el JSX, no una declaración de componente.
|
||||
// Por ejemplo, debería verse así: "<div>Hello from JSX!</div>;"
|
||||
const transformedCode = transform(`<>${code}</>`, {
|
||||
presets: ['react']
|
||||
}).code
|
||||
|
||||
const iframe = iframeRef.current
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document
|
||||
iframeDoc.open()
|
||||
iframeDoc.write(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Preview</title></head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="https://unpkg.com/react/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
|
||||
<script type="text/javascript">
|
||||
${transformedCode}
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(React.createElement(React.Fragment, null, ...React.Children.toArray(React.createElement(() => (${code}), {}))));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
iframeDoc.close()
|
||||
}, [code])
|
||||
|
||||
return <iframe ref={iframeRef} style={{ width: '100%', height: '300px', border: 'none' }} />
|
||||
}
|
||||
|
||||
export default LivePreview
|
145
src/components/Login/FormLogin.jsx
Normal file
145
src/components/Login/FormLogin.jsx
Normal file
@ -0,0 +1,145 @@
|
||||
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 (
|
||||
<form id="frmLogin" name="frmLogin" method="post" onSubmit={(event) => doLogin(event)} noValidate >
|
||||
<div className="flex w-full flex-col space-y-4 px-4 ">
|
||||
<div className="form-group flex flex-col">
|
||||
<div className="via-label">
|
||||
{ i18n.t('login.user') }
|
||||
<div className="inline-flex font-semibold text-red-500">(*)</div>
|
||||
</div>
|
||||
<div className="relative w-full">
|
||||
<input
|
||||
id="usuario"
|
||||
name="usuario"
|
||||
required
|
||||
placeholder=" "
|
||||
type="email"
|
||||
data-pristine-required-message={i18n.t('login.validacion_correo')}
|
||||
className={'via-input'}
|
||||
onChange={(e) => { validate(e) }}
|
||||
/>
|
||||
<button
|
||||
className={'via-append-input shadow-neon'}
|
||||
onClick={(e) => { e.stopPropagation() }}
|
||||
disabled
|
||||
>
|
||||
<UserIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group flex flex-col">
|
||||
<div className={'via-label'}>
|
||||
{ i18n.t('login.password') }
|
||||
<div className="inline-flex font-semibold text-red-500">(*)</div>
|
||||
</div>
|
||||
<div className="relative w-full">
|
||||
<input
|
||||
id="clave"
|
||||
name="clave"
|
||||
required
|
||||
placeholder=" "
|
||||
type={viewPassword === false ? 'password' : 'text'}
|
||||
data-pristine-required-message={i18n.t('login.validacion_password')}
|
||||
className={'via-input'}
|
||||
onChange={(e) => { validate(e) }}
|
||||
/>
|
||||
<button
|
||||
className={'via-append-input shadow-neon'}
|
||||
onClick={(e) => { e.stopPropagation(); e.preventDefault(); setViewPassword(!viewPassword) }}
|
||||
>
|
||||
{ viewPassword === false && <EyeIcon className="h-5 w-5" /> }
|
||||
{ viewPassword === true && <EyeSlashIcon className="h-5 w-5" /> }
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'via-div-actions'}>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!isInputValid}
|
||||
className={`via-button shadow-neon
|
||||
${(isInputValid
|
||||
? '!bg-theme-app-500'
|
||||
: '!bg-danger')}
|
||||
mx-0 w-full justify-center`}
|
||||
>
|
||||
<KeyIcon className="h-5 w-5 pr-2" />
|
||||
{ i18n.t('login.connect') }
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormLogin
|
118
src/components/ResizableDiv/index.jsx
Normal file
118
src/components/ResizableDiv/index.jsx
Normal file
@ -0,0 +1,118 @@
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
|
||||
export default function ResizableDiv ({ children, id, element = {}, disable = false, direction = 'col', min = 20, grow = false, onChange = () => {}, onResize = () => {}, className = '', style = {} }) {
|
||||
const [isMounted, setIsMounted] = useState(false)
|
||||
const [modelo, setModelo] = useState({ ...element })
|
||||
// const [eventUpdate, setEventUpdate] = useState(0)
|
||||
const [initialPosX, setInitialPosX] = useState(null)
|
||||
const [initialSizeX, setInitialSizeX] = useState(null)
|
||||
const [initialPosY, setInitialPosY] = useState(null)
|
||||
const [initialSizeY, setInitialSizeY] = useState(null)
|
||||
|
||||
const divRef = useRef(null)
|
||||
|
||||
const initialX = (e) => {
|
||||
if (divRef != null && divRef.current != null) {
|
||||
const resizable = divRef.current // document.getElementById('Resizable');
|
||||
setInitialPosX(e.clientX)
|
||||
setInitialSizeX(resizable.offsetWidth)
|
||||
}
|
||||
}
|
||||
|
||||
const resizeX = (e) => {
|
||||
// if (divRef != null && divRef.current != null) {
|
||||
// let resizable = divRef.current // document.getElementById('Resizable');
|
||||
// resizable.style.width = `${parseInt(initialSizeX) + parseInt(e.clientX - initialPosX)}px`
|
||||
// }
|
||||
const newWidth = parseInt(initialSizeX) + parseInt(e.clientX - initialPosX)
|
||||
onChange('width', newWidth, modelo)
|
||||
}
|
||||
|
||||
const initialY = (e) => {
|
||||
if (divRef != null && divRef.current != null) {
|
||||
const resizable = divRef.current // document.getElementById('Resizable');
|
||||
setInitialPosY(e.clientY)
|
||||
setInitialSizeY(resizable.offsetHeight)
|
||||
}
|
||||
}
|
||||
|
||||
const resizeY = (e) => {
|
||||
// if (divRef != null && divRef.current != null) {
|
||||
// let resizable = divRef.current // document.getElementById('Resizable');
|
||||
// resizable.style.height = `${parseInt(initialSizeY) + parseInt(e.clientY - initialPosY)}px`
|
||||
// }
|
||||
const newHeight = parseInt(initialSizeY) + parseInt(e.clientY - initialPosY)
|
||||
onChange('height', newHeight, modelo)
|
||||
}
|
||||
|
||||
const applyResize = () => {
|
||||
// console.log(' applyResize ')
|
||||
if (divRef != null && divRef.current != null) {
|
||||
const resizable = divRef.current // document.getElementById('Resizable');
|
||||
if (!grow || grow === false) {
|
||||
if (direction === 'col') {
|
||||
resizable.style.height = `${modelo.height}px`
|
||||
resizable.style.width = '100%'
|
||||
}
|
||||
if (direction === 'row') {
|
||||
resizable.style.height = '100%'
|
||||
resizable.style.width = `${modelo.width}px`
|
||||
}
|
||||
} else {
|
||||
if (direction === 'col') {
|
||||
resizable.style.height = '5rem'
|
||||
resizable.style.width = '100%'
|
||||
}
|
||||
if (direction === 'row') {
|
||||
resizable.style.height = '100%'
|
||||
resizable.style.width = '5rem'
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
onResize()
|
||||
}, 100)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isMounted) {
|
||||
applyResize()
|
||||
}
|
||||
}, [modelo, isMounted])
|
||||
|
||||
useEffect(() => {
|
||||
if (isMounted) {
|
||||
if (JSON.stringify({ ...element, _dz_background_image: null }) !== JSON.stringify({ ...modelo, _dz_background_image: null })) {
|
||||
setModelo({ ...element })
|
||||
}
|
||||
}
|
||||
}, [element])
|
||||
|
||||
useEffect(() => {
|
||||
setIsMounted(true)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={divRef}
|
||||
id={id}
|
||||
className={` relative flex items-center justify-center ${direction === 'col' ? 'w-full' : (grow ? ' w-4 ' : '')} ${direction === 'row' ? 'h-full' : (grow ? ' h-4 ' : '')} ${className}`}
|
||||
style={grow ? { flexGrow: 1, ...style } : { ...style }}>
|
||||
{children}
|
||||
{ disable !== true && direction === 'col' &&
|
||||
<div
|
||||
className=' drag-resize-div-x bg-sky-300 '
|
||||
draggable='true'
|
||||
onDragStart={initialY}
|
||||
onDragEnd={resizeY} />
|
||||
}
|
||||
{ disable !== true && direction === 'row' &&
|
||||
<div
|
||||
className=' drag-resize-div-y bg-sky-300 '
|
||||
draggable='true'
|
||||
onDragStart={initialX}
|
||||
onDragEnd={resizeX} />
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
163
src/components/SessionTimeout/index.jsx
Normal file
163
src/components/SessionTimeout/index.jsx
Normal file
@ -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 &&
|
||||
<Transition appear show as={Fragment}>
|
||||
<Dialog as='div' className='relative' style={{ zIndex: 999 }} onClose={() => { }}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0'
|
||||
enterTo='opacity-100'
|
||||
leave='ease-in duration-200'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0'
|
||||
>
|
||||
<div className='fixed inset-0 bg-black/25' />
|
||||
</Transition.Child>
|
||||
|
||||
<div className='fixed inset-0 overflow-y-auto'>
|
||||
<div className='flex min-h-full items-center justify-center p-4 text-center'>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0 scale-95'
|
||||
enterTo='opacity-100 scale-100'
|
||||
leave='ease-in duration-200'
|
||||
leaveFrom='opacity-100 scale-100'
|
||||
leaveTo='opacity-0 scale-95'
|
||||
>
|
||||
<Dialog.Panel className='w-full max-w-md overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all'>
|
||||
<Dialog.Title
|
||||
as='h3'
|
||||
className='text-lg font-medium leading-6 text-gray-900'
|
||||
>
|
||||
{i18n.t('common.sessionToFinish')}
|
||||
</Dialog.Title>
|
||||
<div className='mt-2'>
|
||||
<p className='text-lg font-extrabold text-gray-500'>
|
||||
{countTimer}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='mt-4 flex w-full flex-row-reverse'>
|
||||
<button
|
||||
type='button'
|
||||
className='mr-2 inline-flex justify-center rounded-md border border-transparent bg-red-100 px-4 py-2 text-sm font-medium text-red-900 hover:bg-red-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2'
|
||||
onClick={() => { stopIdleMonitor() }}
|
||||
>
|
||||
{i18n.t('common.sessionContinue')}
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
SessionTimeout.propTypes = {
|
||||
i18n: PropTypes.object,
|
||||
user: PropTypes.object,
|
||||
onTimeout: PropTypes.func,
|
||||
onCancelTimeout: PropTypes.func
|
||||
}
|
||||
|
||||
SessionTimeout.defaultProps = {
|
||||
i18n: { t: () => { return 'txtNone' } },
|
||||
user: {},
|
||||
onTimeout: () => {},
|
||||
onCancelTimeout: () => {}
|
||||
}
|
468
src/components/vComponents/Sidebar/index.jsx
Normal file
468
src/components/vComponents/Sidebar/index.jsx
Normal file
@ -0,0 +1,468 @@
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { ChevronDownIcon, ArrowLeftOnRectangleIcon, MagnifyingGlassIcon, XMarkIcon, Bars3Icon } from '@heroicons/react/20/solid'
|
||||
import * as iconsFc from 'react-icons/fc'
|
||||
import * as iconsMd from 'react-icons/md'
|
||||
import * as HeroIcons from '@heroicons/react/24/solid'
|
||||
import mq from 'js-mq'
|
||||
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
Sidebar.propTypes = {
|
||||
sidebarOpen: PropTypes.bool,
|
||||
setSidebarOpen: PropTypes.func,
|
||||
menu: PropTypes.array,
|
||||
sidebarStyles: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
optionStyles: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
iconOptionStyles: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
suboptionStyles: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
iconSuboptionStyles: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
onClickLogout: PropTypes.func,
|
||||
companyObj: PropTypes.object,
|
||||
userObj: PropTypes.object,
|
||||
setTitle: PropTypes.func,
|
||||
i18n: PropTypes.object,
|
||||
router: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
environment: PropTypes.object,
|
||||
presets: PropTypes.object,
|
||||
appTitleStyles: PropTypes.string,
|
||||
userInfoStyles: PropTypes.string
|
||||
}
|
||||
|
||||
// funcion que agrupa un vector de objetos por un campo y lo convierte en arbol
|
||||
const nest = (items, idMenu = null, link = 'id_menu_padre') =>
|
||||
items
|
||||
.filter(item => item[link] === idMenu)
|
||||
.map(item => ({
|
||||
...item,
|
||||
children:
|
||||
nest(items, item.id_menu).length <= 0
|
||||
? undefined
|
||||
: nest(items, item.id_menu)
|
||||
}))
|
||||
|
||||
const fontSizeClass = ' xs:text-md sm:text-md md:text-md lg:text-base xl:text-base '
|
||||
|
||||
// From https://reactjs.org/docs/hooks-state.html
|
||||
export default function Sidebar ({ sidebarOpen, setSidebarOpen = () => {}, menu, sidebarStyles, optionStyles = fontSizeClass, iconOptionStyles, suboptionStyles = fontSizeClass, iconSuboptionStyles, onClickLogout, companyObj, userObj, setTitle, i18n = { t: () => { return 'txtNone' } }, router = [], environment = {}, presets, appTitleStyles = '', userInfoStyles = fontSizeClass, hideInputSearch = false }) {
|
||||
const trigger = useRef(null)
|
||||
const sidebar = useRef(null)
|
||||
const [options, setOptions] = useState([])
|
||||
const [isMobile, setIsMobile] = useState(false)
|
||||
const [searchText, setSearchText] = useState('')
|
||||
|
||||
const getFontSize = (base) => {
|
||||
if (base != null &&
|
||||
(base.includes('text-xs') || base.includes('text-sm') || base.includes('text-base') || base.includes('text-md') || base.includes('text-lg') || base.includes('text-xl'))) {
|
||||
return ''
|
||||
}
|
||||
return fontSizeClass
|
||||
}
|
||||
|
||||
// close on click outside
|
||||
useEffect(() => {
|
||||
const clickHandler = ({ target }) => {
|
||||
if (!sidebar.current || !trigger.current) return
|
||||
if (!sidebarOpen || sidebar.current.contains(target) || trigger.current.contains(target)) return
|
||||
setSidebarOpen(false)
|
||||
}
|
||||
document.addEventListener('click', clickHandler)
|
||||
return () => document.removeEventListener('click', clickHandler)
|
||||
})
|
||||
|
||||
// close if the esc key is pressed
|
||||
useEffect(() => {
|
||||
const keyHandler = ({ keyCode }) => {
|
||||
if (!sidebarOpen || keyCode !== 27) return
|
||||
setSidebarOpen(false)
|
||||
}
|
||||
document.addEventListener('keydown', keyHandler)
|
||||
return () => document.removeEventListener('keydown', keyHandler)
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const menus = nest(menu)
|
||||
// console.log('menu', menus)
|
||||
setOptions(menus)
|
||||
}, [menu])
|
||||
|
||||
useEffect(() => {
|
||||
registrarBreckpoint()
|
||||
}, [])
|
||||
|
||||
/* eslint-disable */
|
||||
const registrarBreckpoint = () => {
|
||||
if (typeof document !== undefined) {
|
||||
try {
|
||||
mq.register([
|
||||
/* eslint-enable */
|
||||
{ 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}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getSidebarClass = () => {
|
||||
let resultCss = ''
|
||||
if (isMobile === true) {
|
||||
resultCss = `flex flex-col z-60 top-1/3 h-4/6 overflow-y-auto w-full shrink-0 p-4 rounded-md duration-[400ms] ease-in-out
|
||||
${sidebarOpen ? 'translate-y-0 fixed' : ' transform translate-y-[100vh] fixed'} `
|
||||
} else {
|
||||
resultCss = `flex flex-col z-40 left-0 top-0 h-screen overflow-y-auto w-64 shrink-0 p-4 transition-all duration-200 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0 relative' : '-translate-x-64 absolute'} `
|
||||
}
|
||||
return resultCss
|
||||
}
|
||||
|
||||
const setSelected = (option) => {
|
||||
const menus = options.slice()
|
||||
const idx = menus.indexOf(option)
|
||||
option.isSelected = option.isSelected ? !option.isSelected : true
|
||||
menus.splice(idx, 1, option)
|
||||
setOptions(menus)
|
||||
if (option.path && option.path !== null) {
|
||||
setTitle(option.title)
|
||||
setSidebarOpen(false)
|
||||
router.push(`${option.path}/${environment.getTime()}`)
|
||||
}
|
||||
}
|
||||
|
||||
const setSelectedSubOption = (option, suboption) => {
|
||||
const menus = options.slice()
|
||||
const idxOption = menus.indexOf(option)
|
||||
const idxSubOption = option.children.indexOf(suboption)
|
||||
suboption.isSelected = suboption.isSelected ? !suboption.isSelected : true
|
||||
option.children.splice(idxSubOption, 1, suboption)
|
||||
setTitle(suboption.title)
|
||||
menus.splice(idxOption, 1, option)
|
||||
setOptions(menus)
|
||||
setSidebarOpen(false)
|
||||
router.push(`${suboption.path}/${environment.getTime()}`)
|
||||
}
|
||||
|
||||
/* eslint-disable */
|
||||
const SubMenuOption = ({ option }) => {
|
||||
|
||||
if (option.children && option.children.length > 0 && option.isSelected === true) {
|
||||
return (
|
||||
<ul id='dropdown-example' className='ml-1 py-1 space-y-1'>
|
||||
{option.children.map((suboption) => {
|
||||
if (suboption.type === 'divisor') {
|
||||
return (<li key={suboption.id_menu}><hr /></li>)
|
||||
}
|
||||
//Iconos subMenu
|
||||
const getIcono = (item) => {
|
||||
try {
|
||||
const IconComponent = HeroIcons[item.icon]
|
||||
if (IconComponent != null) {
|
||||
return IconComponent
|
||||
}
|
||||
const IconComponentMd = iconsMd[item.icon]
|
||||
if (IconComponentMd != null) {
|
||||
return IconComponentMd
|
||||
}
|
||||
const IconComponentFC = iconsFc[item.icon]
|
||||
if (IconComponentFC != null) {
|
||||
return IconComponentFC
|
||||
}
|
||||
return null
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
let Icono = null
|
||||
if (suboption.icon) {
|
||||
Icono = getIcono(suboption)
|
||||
}
|
||||
return (
|
||||
<li key={suboption.id_menu} onClick={() => setSelectedSubOption(option, suboption)} title={suboption.title} className='cursor-pointer'>
|
||||
<div
|
||||
className={`flex items-center p-2 pl-3 w-full truncate overflow-hidden rounded-lg transition duration-75 group cursor-pointer ${Array.isArray(suboptionStyles) ? suboptionStyles.join(' ') : suboptionStyles} ${getFontSize(suboptionStyles)}`}
|
||||
title={suboption.title}
|
||||
>
|
||||
|
||||
{Icono && <Icono className={`h-7 w-7 text-white fill-current ${Array.isArray(iconSuboptionStyles) ? iconSuboptionStyles.join(' ') : iconSuboptionStyles}`} />}
|
||||
|
||||
<div className='inline-flex truncate text-ellipsis'>
|
||||
{/* {suboption.title} */}
|
||||
{suboption.i18n ? i18n.t(suboption.i18n) : suboption.title}
|
||||
|
||||
</div>
|
||||
{suboption.badge && (
|
||||
<span className='ml-1 inline-flex items-center justify-center rounded-full bg-red-600 px-2 py-1 text-xs font-bold leading-none text-red-100'>{suboption.badge}</span>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
const MenuOption = ({ option }) => {
|
||||
// Iconos Menu
|
||||
const getIcono = (item) => {
|
||||
try {
|
||||
const IconComponent = HeroIcons[item.icon]
|
||||
if (IconComponent != null) {
|
||||
return IconComponent
|
||||
}
|
||||
const IconComponentMd = iconsMd[item.icon]
|
||||
if (IconComponentMd != null) {
|
||||
return IconComponentMd
|
||||
}
|
||||
const IconComponentFC = iconsFc[item.icon]
|
||||
if (IconComponentFC != null) {
|
||||
return IconComponentFC
|
||||
}
|
||||
return null
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
let Icono = null
|
||||
if (option.icon) {
|
||||
Icono = getIcono(option)
|
||||
}
|
||||
if (option.type === 'titulo') {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type='button'
|
||||
className={`group flex w-full items-center rounded-lg p-2 transition duration-75 ${Array.isArray(optionStyles) ? optionStyles.join(' ') : optionStyles} ${getFontSize(optionStyles)}`}
|
||||
aria-controls='dropdown-example' data-collapse-toggle='dropdown-example'
|
||||
title={option.title}
|
||||
>
|
||||
<div className='ml-3 flex-1 truncate whitespace-nowrap text-left'>
|
||||
{/* {option.title} */}
|
||||
{option.i18n ? i18n.t(option.i18n) : option.title}
|
||||
</div>
|
||||
{option.badge && (
|
||||
<span className='ml-1 inline-flex items-center justify-center rounded-full bg-red-600 px-2 py-1 text-xs font-bold leading-none text-red-100'>{option.badge}</span>
|
||||
) }
|
||||
{Icono && <Icono className={`h-7 w-7 fill-current text-white ${Array.isArray(iconOptionStyles) ? iconOptionStyles.join(' ') : iconOptionStyles}`} />}
|
||||
</button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type='button'
|
||||
className={`group flex w-full items-center rounded-lg p-2 transition duration-75 ${Array.isArray(optionStyles) ? optionStyles.join(' ') : optionStyles} ${getFontSize(optionStyles)}`}
|
||||
aria-controls='dropdown-example' data-collapse-toggle='dropdown-example'
|
||||
title={option.title}
|
||||
onClick={() => setSelected(option)}
|
||||
>
|
||||
|
||||
{Icono && <Icono className={`h-7 w-7 fill-current text-white ${Array.isArray(iconOptionStyles) ? iconOptionStyles.join(' ') : iconOptionStyles}`} />}
|
||||
|
||||
<div className='ml-3 flex-1 truncate whitespace-nowrap text-left'>
|
||||
{/* {option.title} */}
|
||||
{option.i18n ? i18n.t(option.i18n) : option.title}
|
||||
</div>
|
||||
{option.badge && (
|
||||
<span className='ml-1 inline-flex items-center justify-center rounded-full bg-red-600 px-2 py-1 text-xs font-bold leading-none text-red-100'>{option.badge}</span>
|
||||
) }
|
||||
{option.children && option.children.length > 0 && <ChevronDownIcon className='h-6 w-6' />}
|
||||
</button>
|
||||
<SubMenuOption option={option} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
MenuOption.propTypes = {
|
||||
option: PropTypes.object
|
||||
}
|
||||
|
||||
const getFilteredOptions = (options, searchText, originalMenu) => {
|
||||
const searchLower = searchText.toLowerCase()
|
||||
|
||||
const cloneWithIsSelected = (items, selectedItems) => {
|
||||
return items.map(item => {
|
||||
const selectedItem = selectedItems.find(si => si.id_menu === item.id_menu)
|
||||
const isSelected = selectedItem ? selectedItem.isSelected : false
|
||||
const children = item.children ? cloneWithIsSelected(item.children, selectedItems) : []
|
||||
return { ...item, isSelected, children }
|
||||
})
|
||||
}
|
||||
|
||||
const searchAndMergeItems = (items, isChildMatch = false) => {
|
||||
return items.reduce((acc, item) => {
|
||||
const matchSelf = item.title.toLowerCase().includes(searchLower)
|
||||
const children = item.children ? searchAndMergeItems(item.children, matchSelf || isChildMatch) : []
|
||||
const newItem = { ...item, children }
|
||||
|
||||
if (matchSelf || children.length > 0 || isChildMatch) {
|
||||
const existingIndex = acc.findIndex(e => e.id_menu === item.id_menu)
|
||||
if (existingIndex > -1) {
|
||||
acc[existingIndex] = { ...acc[existingIndex], isSelected: newItem.isSelected || acc[existingIndex].isSelected }
|
||||
} else {
|
||||
acc.push(newItem)
|
||||
}
|
||||
}
|
||||
|
||||
return acc
|
||||
}, [])
|
||||
}
|
||||
|
||||
if (searchText) {
|
||||
return searchAndMergeItems(options)
|
||||
} else {
|
||||
const selectedItems = options.filter(item => item.isSelected)
|
||||
return cloneWithIsSelected(originalMenu, selectedItems)
|
||||
}
|
||||
}
|
||||
|
||||
const originalMenu = nest(menu)
|
||||
|
||||
const filteredOptions = getFilteredOptions(options, searchText, originalMenu)
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Sidebar backdrop (mobile only) bg-slate-900 */}
|
||||
<div
|
||||
className={`fixed inset-0 bg-opacity-30 z-40 md:hidden md:z-auto transition-opacity duration-200
|
||||
${sidebarOpen ? 'opacity-100' : 'pointer-events-none opacity-0'} ${Array.isArray(sidebarStyles) ? sidebarStyles.join(' ') : sidebarStyles}`}
|
||||
aria-hidden='true'
|
||||
onClick={() => { setSidebarOpen(!sidebarOpen) }}
|
||||
/>
|
||||
{/* Sidebar */}
|
||||
{/* lg:translate-x-0 lg:w-20 lg:static lg:left-auto lg:top-auto lg:overflow-y-auto lg:sidebar-expanded:!w-64 2xl:!w-64 */}
|
||||
<div
|
||||
id='sidebar'
|
||||
ref={sidebar}
|
||||
className={` ${getSidebarClass()} ${Array.isArray(sidebarStyles) ? sidebarStyles.join(' ') : sidebarStyles}`}
|
||||
>
|
||||
{/* Expand / collapse button */}
|
||||
<div className='fixed right-6 top-4 justify-end pt-0 md:hidden '>
|
||||
<div className='px-3 py-2'>
|
||||
<button onClick={() => setSidebarOpen(!sidebarOpen)}>
|
||||
<div className='sr-only'>Expand / collapse sidebar</div>
|
||||
<XMarkIcon className={`text-${presets.theme}-600 h-6 w-6 rounded-md bg-white/50`} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar header */}
|
||||
<div className='mb-10 flex justify-between pr-3 sm:px-2'>
|
||||
{/* Close button */}
|
||||
{/* <button
|
||||
ref={trigger}
|
||||
className={`absolute cursor-pointer -right-3 top-9 w-7 border-dark-purple z-50
|
||||
border-2 rounded-full ${!sidebarOpen && 'rotate-180'}`}
|
||||
onClick={() => setSidebarOpen(!sidebarOpen)}
|
||||
aria-controls='sidebar'
|
||||
aria-expanded={sidebarOpen}
|
||||
>
|
||||
<div className='sr-only'>Close sidebar</div>
|
||||
{sidebarOpen
|
||||
? <ArrowLeftIcon className='w-6 h-6 fill-current text-white' />
|
||||
: <ArrowRightIcon className='w-6 h-6 fill-current text-white' />
|
||||
}
|
||||
</button> */}
|
||||
{/* Logo */}
|
||||
<div className='flex items-center gap-x-2' title='Versión 3.0.10'>
|
||||
<img
|
||||
src={presets.images.icon}
|
||||
className={`h-12 w-12 cursor-pointer duration-500 ${
|
||||
sidebarOpen && 'rotate-[360deg]'
|
||||
}`}
|
||||
alt={presets.appTitle}
|
||||
width={100}
|
||||
height={100}
|
||||
/>
|
||||
<h1 className={`origin-left text-xl font-medium text-white duration-200 ${!sidebarOpen && 'scale-0'} ${appTitleStyles} `}>
|
||||
{presets.appTitle}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
{/* user Info */}
|
||||
<div className={` flex w-full flex-col space-y-0 border-b-2 border-gray-300 text-gray-50 ${userInfoStyles} ${getFontSize(userInfoStyles)}`}>
|
||||
<div className='px-2 font-medium '> {userObj ? userObj.compania : ''} </div>
|
||||
<div className='px-2'>{userObj ? userObj.nombre_usuario : ''}</div>
|
||||
<div className='break-all px-2 '>{userObj ? userObj.email : ''}</div>
|
||||
</div>
|
||||
|
||||
{/* Links */}
|
||||
<div className={`my-0 overflow-y-auto rounded px-2 py-4 ${Array.isArray(sidebarStyles) ? sidebarStyles.join(' ') : sidebarStyles}`}>
|
||||
|
||||
{/* Search */}
|
||||
{ hideInputSearch !== true &&
|
||||
<div className='relative -mt-2 mb-1 flex h-9 justify-between'>
|
||||
<input
|
||||
id='searchText'
|
||||
name='searchText'
|
||||
value={searchText}
|
||||
type='search'
|
||||
className='via-input-search-sidebar focus:!outline-none focus:!ring-0'
|
||||
onInput={(e) => { setSearchText(e.target.value) }}
|
||||
onChange={(e) => { setSearchText(e.target.value) }}
|
||||
/>
|
||||
<button
|
||||
className='via-button-search-sidebar'
|
||||
onClick={(e) => { e.stopPropagation() }}
|
||||
disabled
|
||||
>
|
||||
<MagnifyingGlassIcon className='h-5 w-5' />
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<ul className='space-y-2'>
|
||||
{filteredOptions.map((option) => {
|
||||
/* eslint-disable */
|
||||
return (
|
||||
<li key={option.id_menu + '_li'}>
|
||||
{option.type === 'divisor' && <hr key={option.id_menu} />}
|
||||
{option.type !== 'divisor' && <MenuOption option={option} key={option.id_menu} />}
|
||||
</li>
|
||||
)
|
||||
/* eslint-enable */
|
||||
})}
|
||||
<hr />
|
||||
<li>
|
||||
<button
|
||||
type='button'
|
||||
className={`group flex w-full items-center rounded-lg p-2 transition duration-75 ${Array.isArray(optionStyles) ? optionStyles.join(' ') : optionStyles} ${getFontSize(optionStyles)}`}
|
||||
aria-controls='dropdown-example' data-collapse-toggle='dropdown-example'
|
||||
onClick={() => onClickLogout()}
|
||||
>
|
||||
<ArrowLeftOnRectangleIcon className='h-6 w-6 pr-2' />
|
||||
<div className='ml-3 flex-1 whitespace-nowrap text-left'>{i18n.t('common.logout')}</div>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{ isMobile === true &&
|
||||
<div className="fixed bottom-3 right-3 z-60" v-if="isMobile" >
|
||||
<button
|
||||
id="vtDrawerButton"
|
||||
className={`bg-${presets.theme}-600 hover:bg-${presets.theme}-700 focus:shadow-outline-purple flex items-center justify-between rounded-lg border border-transparent p-2 font-normal leading-5 text-white transition-colors duration-150 focus:outline-none active:bg-purple-600`}
|
||||
aria-label="Like"
|
||||
onClick={() => { setSidebarOpen(!sidebarOpen) }}
|
||||
>
|
||||
<Bars3Icon className='h-6 w-6' />
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
115
src/components/widgets/UserOptionsMenu.jsx
Normal file
115
src/components/widgets/UserOptionsMenu.jsx
Normal file
@ -0,0 +1,115 @@
|
||||
import React from 'react'
|
||||
import { Menu } from '@headlessui/react'
|
||||
import { MdOutlineCheckBox, MdOutlineCheckBoxOutlineBlank, MdOutlineLightMode, MdDarkMode } from 'react-icons/md'
|
||||
import { TbColorPicker, TbLanguage } from 'react-icons/tb'
|
||||
|
||||
const UserOptionsMenu = ({ setPreferences, theme, i18n }) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-center text-gray-900 dark:text-white">
|
||||
<TbColorPicker className="mr-2 h-5 w-5" aria-hidden="true" />
|
||||
Tema
|
||||
</div>
|
||||
<div className="mb-2 grid grid-cols-2 gap-2 p-1">
|
||||
<Menu.Item>
|
||||
<button
|
||||
className={`${
|
||||
theme === 'light'
|
||||
? 'bg-theme-app-500 text-white dark:bg-theme-app-500'
|
||||
: 'bg-theme-app-50 text-gray-900 transition-colors duration-[350ms] ease-in-out hover:bg-theme-app-100'
|
||||
} group flex w-full items-center rounded-md p-2 text-sm`}
|
||||
onClick={async () => {
|
||||
setPreferences('light')
|
||||
}}
|
||||
>
|
||||
<MdOutlineLightMode className="mr-2 h-5 w-5" aria-hidden="true" />
|
||||
Claro
|
||||
</button>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<button
|
||||
className={`${
|
||||
theme === 'dark'
|
||||
? 'bg-theme-app-500 text-white dark:bg-theme-app-500'
|
||||
: 'bg-theme-app-50 text-gray-900 transition-colors duration-[350ms] ease-in-out hover:bg-theme-app-100'
|
||||
} group flex w-full items-center rounded-md p-2 text-sm`}
|
||||
onClick={() => {
|
||||
setPreferences('dark')
|
||||
}}
|
||||
>
|
||||
<MdDarkMode className="mr-2 h-5 w-5" aria-hidden="true" />
|
||||
Oscuro
|
||||
</button>
|
||||
</Menu.Item>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center text-gray-900 dark:text-white">
|
||||
<TbLanguage className="mr-2 h-5 w-5" aria-hidden="true" />
|
||||
Idioma
|
||||
</div>
|
||||
|
||||
<div className="mb-2 grid grid-cols-2 gap-2 p-1">
|
||||
<Menu.Item>
|
||||
<button
|
||||
className={`${
|
||||
i18n.activeLocale === 'es'
|
||||
? 'bg-theme-app-500 text-white dark:bg-theme-app-500'
|
||||
: 'bg-theme-app-50 text-gray-900 transition-colors duration-[350ms] ease-in-out hover:bg-theme-app-100'
|
||||
} group flex w-full items-center rounded-md p-2 text-sm`}
|
||||
onClick={async () => {
|
||||
setPreferences('es')
|
||||
}}
|
||||
>
|
||||
{
|
||||
i18n.activeLocale === 'es'
|
||||
? (
|
||||
<MdOutlineCheckBox
|
||||
className="mr-2 h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<MdOutlineCheckBoxOutlineBlank
|
||||
className="mr-2 h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)
|
||||
}
|
||||
Español
|
||||
</button>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<button
|
||||
className={`${
|
||||
i18n.activeLocale === 'en'
|
||||
? 'bg-theme-app-500 text-white dark:bg-theme-app-500'
|
||||
: 'bg-theme-app-50 text-gray-900 transition-colors duration-[350ms] ease-in-out hover:bg-theme-app-100'
|
||||
} group flex w-full items-center rounded-md p-2 text-sm`}
|
||||
onClick={() => {
|
||||
setPreferences('en')
|
||||
}}
|
||||
>
|
||||
{
|
||||
i18n.activeLocale === 'en'
|
||||
? (
|
||||
<MdOutlineCheckBox
|
||||
className="mr-2 h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<MdOutlineCheckBoxOutlineBlank
|
||||
className='mr-2 h-5 w-5'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
)
|
||||
}
|
||||
Ingles
|
||||
</button>
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default UserOptionsMenu
|
72
src/data/menu.json
Normal file
72
src/data/menu.json
Normal file
@ -0,0 +1,72 @@
|
||||
[
|
||||
{
|
||||
"id_menu": "v-components",
|
||||
"title": "v-components",
|
||||
"descripcion": null,
|
||||
"icon": "FcDeployment",
|
||||
"path": null,
|
||||
"orden": 1,
|
||||
"id_menu_padre": null,
|
||||
"badge": null
|
||||
},
|
||||
{
|
||||
"id_menu": "data-table",
|
||||
"title": "DataTable",
|
||||
"descripcion": null,
|
||||
"icon": "FcDataSheet",
|
||||
"path": "/seguridad/compania",
|
||||
"orden": 1.1,
|
||||
"id_menu_padre": "v-components",
|
||||
"badge": null
|
||||
},
|
||||
{
|
||||
"id_menu": "data-form",
|
||||
"title": "DataFrom",
|
||||
"descripcion": null,
|
||||
"icon": "FcKindle",
|
||||
"path": "/seguridad/departamento",
|
||||
"orden": 1.2,
|
||||
"id_menu_padre": "v-components",
|
||||
"badge": null
|
||||
},
|
||||
{
|
||||
"id_menu": "via-ui",
|
||||
"title": "via-ui",
|
||||
"descripcion": null,
|
||||
"icon": "FcCloseUpMode",
|
||||
"path": "",
|
||||
"orden": 2,
|
||||
"id_menu_padre": null,
|
||||
"badge": "New"
|
||||
},
|
||||
{
|
||||
"id_menu": "tooltip",
|
||||
"title": "Tooltip",
|
||||
"descripcion": null,
|
||||
"icon": "FcMms",
|
||||
"path": "/via-ui/tooltip",
|
||||
"orden": 2.1,
|
||||
"id_menu_padre": "via-ui",
|
||||
"badge": null
|
||||
},
|
||||
{
|
||||
"id_menu": "tab",
|
||||
"title": "Tab",
|
||||
"descripcion": null,
|
||||
"icon": "FcTemplate",
|
||||
"path": "/via-ui/tab",
|
||||
"orden": 2.1,
|
||||
"id_menu_padre": "via-ui",
|
||||
"badge": null
|
||||
},
|
||||
{
|
||||
"id_menu": "test",
|
||||
"title": "Test",
|
||||
"descripcion": null,
|
||||
"icon": "FcIdea",
|
||||
"path": "/via-ui/test",
|
||||
"orden": 2.1,
|
||||
"id_menu_padre": "via-ui",
|
||||
"badge": null
|
||||
}
|
||||
]
|
103
src/helper/clientApi.js
Normal file
103
src/helper/clientApi.js
Normal file
@ -0,0 +1,103 @@
|
||||
import forge from 'node-forge'
|
||||
|
||||
const constants = {
|
||||
urlWebApi: process.env.urlWebApi,
|
||||
urlUploadApi: process.env.urlUploadApi,
|
||||
publicKey: process.env.publicKey
|
||||
}
|
||||
|
||||
const uris = {
|
||||
procedure: '',
|
||||
upload: '/upload'
|
||||
}
|
||||
|
||||
const encryptRsa = function (obj) {
|
||||
const encoded = forge.util.encodeUtf8(obj)
|
||||
const publicKey = forge.pki.publicKeyFromPem(constants.publicKey)
|
||||
const encrypted = publicKey.encrypt(encoded, 'RSA-OAEP', {
|
||||
md: forge.md.sha256.create(),
|
||||
mgf1: forge.mgf1.create()
|
||||
})
|
||||
const base64 = forge.util.encode64(encrypted)
|
||||
return base64
|
||||
}
|
||||
|
||||
const callWs = async (uri, json) => {
|
||||
// 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 }
|
9
src/hooks/useGlobalContainer.js
Normal file
9
src/hooks/useGlobalContainer.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { useContext } from 'react'
|
||||
import { LayoutContext } from '@/layout/ResponsiveContainer'
|
||||
|
||||
const useGlobalContainer = () => {
|
||||
const container = useContext(LayoutContext)
|
||||
return container
|
||||
}
|
||||
|
||||
export default useGlobalContainer
|
54
src/hooks/useGlobalFilters.js
Normal file
54
src/hooks/useGlobalFilters.js
Normal file
@ -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
|
13
src/hooks/useHasMounted.js
Normal file
13
src/hooks/useHasMounted.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
const useHasMounted = () => {
|
||||
const [hasMounted, setHasMounted] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setHasMounted(true)
|
||||
}, [])
|
||||
|
||||
return hasMounted
|
||||
}
|
||||
|
||||
export default useHasMounted
|
9
src/hooks/useI18n.js
Normal file
9
src/hooks/useI18n.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { useContext } from 'react'
|
||||
import { I18nContext } from '@/plugins/i18nContext'
|
||||
|
||||
const useI18n = () => {
|
||||
const i18n = useContext(I18nContext)
|
||||
return i18n
|
||||
}
|
||||
|
||||
export default useI18n
|
29
src/hooks/useKeyPress.js
Normal file
29
src/hooks/useKeyPress.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
const useKeyPress = (targetKeys, onKeyPress, preventDefault = false) => {
|
||||
useEffect(() => {
|
||||
const handleKeyPress = (event) => {
|
||||
const keys = Array.isArray(targetKeys) ? targetKeys : [targetKeys]
|
||||
|
||||
if (keys.every(key => {
|
||||
if (key === 'Meta' || key === 'Control' || key === 'Alt' || key === 'Shift') {
|
||||
return event.getModifierState(key)
|
||||
} else {
|
||||
return event.key === key
|
||||
}
|
||||
})) {
|
||||
if (preventDefault) {
|
||||
event.preventDefault()
|
||||
}
|
||||
onKeyPress()
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handleKeyPress)
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyPress)
|
||||
}
|
||||
}, [targetKeys, onKeyPress, preventDefault])
|
||||
}
|
||||
|
||||
export default useKeyPress
|
9
src/hooks/useLoading.js
Normal file
9
src/hooks/useLoading.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { useContext } from 'react'
|
||||
import { LoadingContext } from '@/plugins/LoadingContext'
|
||||
|
||||
const useLoading = () => {
|
||||
const loading = useContext(LoadingContext)
|
||||
return loading
|
||||
}
|
||||
|
||||
export default useLoading
|
120
src/hooks/useLocalStorage.js
Normal file
120
src/hooks/useLocalStorage.js
Normal file
@ -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 (
|
||||
* <div>
|
||||
* <input
|
||||
* type="text"
|
||||
* value={name}
|
||||
* onChange={(e) => setName(e.target.value)}
|
||||
* />
|
||||
* <p>El nombre es: {name}</p>
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
*/
|
||||
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
|
76
src/hooks/useRoute.js
Normal file
76
src/hooks/useRoute.js
Normal file
@ -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
|
||||
}
|
||||
|
||||
return (
|
||||
<RouteContext.Provider value={value}>
|
||||
{children}
|
||||
</RouteContext.Provider>
|
||||
)
|
||||
}
|
35
src/hooks/useScreenType.js
Normal file
35
src/hooks/useScreenType.js
Normal file
@ -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
|
46
src/hooks/useStore.ts
Normal file
46
src/hooks/useStore.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { createStore } from 'react-simple-hook-store'
|
||||
|
||||
interface IState {
|
||||
title: string;
|
||||
theme: string;
|
||||
usuarioPermisos: object | null;
|
||||
valueEasyMDE: string;
|
||||
}
|
||||
|
||||
interface IActions {
|
||||
setTitle: (newState: string) => void;
|
||||
setTheme: (newState: string) => void;
|
||||
setUsuarioPermisos: (newState: object | null) => void;
|
||||
setValueForEasyMDE: (newState: string) => void;
|
||||
}
|
||||
|
||||
export const { useStore, store } = createStore<IState, IActions>(
|
||||
{
|
||||
title: '',
|
||||
theme: 'light',
|
||||
usuarioPermisos: null,
|
||||
valueEasyMDE: ''
|
||||
},
|
||||
{
|
||||
setTitle: (store, newState) => {
|
||||
store.setState({
|
||||
title: newState
|
||||
})
|
||||
},
|
||||
setTheme: (store, newState) => {
|
||||
store.setState({
|
||||
theme: newState
|
||||
})
|
||||
},
|
||||
setUsuarioPermisos: (store, newState) => {
|
||||
store.setState({
|
||||
usuarioPermisos: newState
|
||||
})
|
||||
},
|
||||
setValueForEasyMDE: (store, newState) => {
|
||||
store.setState({
|
||||
valueEasyMDE: newState
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
266
src/layout/ResponsiveContainer.jsx
Normal file
266
src/layout/ResponsiveContainer.jsx
Normal file
@ -0,0 +1,266 @@
|
||||
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'
|
||||
|
||||
import menuData from '@/data/menu.json'
|
||||
|
||||
export const LayoutContext = createContext()
|
||||
|
||||
const Footer = dynamic(() => { return import('vComponents/Footer') }, { ssr: false })
|
||||
const Sidebar = dynamic(() => { return import('@/components/vComponents/Sidebar') }, { ssr: false })
|
||||
const Navbar = dynamic(() => { return import('vComponents/Navbar') }, { ssr: false })
|
||||
|
||||
const ResponsiveContainer = ({ children }) => {
|
||||
const router = useRouter()
|
||||
const loading = useLoading()
|
||||
const hasMounted = useHasMounted()
|
||||
const i18n = useI18n()
|
||||
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true)
|
||||
const [menu, setMenu] = useState([])
|
||||
const [userObj, setUserObj] = useState()
|
||||
const [token, setToken] = useState('')
|
||||
const [constanteObj, setConstanteObj] = useState('')
|
||||
const [appVersion, setAppVersion] = useState('1.0.0')
|
||||
|
||||
const [title, setTitle] = useStore(s => s.title, a => a.setTitle)
|
||||
const [theme, setTheme] = useStore(s => s.theme, a => a.setTheme)
|
||||
|
||||
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 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])
|
||||
const options = menuData
|
||||
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(() => {
|
||||
getMenu()
|
||||
}, [token])
|
||||
|
||||
// when page is mounted
|
||||
useEffect(() => {
|
||||
if (hasMounted) {
|
||||
getEnv().then(() => {
|
||||
getMenu()
|
||||
})
|
||||
setAppVersion(process.env.NEXT_PUBLIC_VERSION)
|
||||
}
|
||||
}, [hasMounted])
|
||||
|
||||
useEffect(() => {
|
||||
const handleStart = (url) => {
|
||||
// tienePermisosParaRuta(url)
|
||||
loading.start()
|
||||
}
|
||||
|
||||
const handleComplete = () => {
|
||||
loading.stop()
|
||||
}
|
||||
|
||||
router.events.on('routeChangeStart', handleStart)
|
||||
router.events.on('routeChangeComplete', handleComplete)
|
||||
router.events.on('routeChangeError', handleComplete)
|
||||
|
||||
return () => {
|
||||
router.events.off('routeChangeStart', handleStart)
|
||||
router.events.off('routeChangeComplete', handleComplete)
|
||||
router.events.off('routeChangeError', handleComplete)
|
||||
}
|
||||
}, [router])
|
||||
|
||||
return (
|
||||
<LayoutContext.Provider value={containerWrapper}>
|
||||
|
||||
<div className={`flex h-screen overflow-hidden text-theme-text-principal ${getTheme()}`}>
|
||||
{/* Sidebar */}
|
||||
{ userObj && userObj.nombre_usuario &&
|
||||
<Sidebar
|
||||
sidebarOpen={sidebarOpen}
|
||||
setSidebarOpen={setSidebarOpen}
|
||||
menu={menu}
|
||||
sidebarStyles={'via-bg-sidebar'}
|
||||
appTitleStyles='via-app-title-sidebar'
|
||||
userInfoStyles='via-user-info-sidebar'
|
||||
optionStyles={'via-options-sidebar'}
|
||||
iconOptionStyles={'via-icons-sidebar'}
|
||||
suboptionStyles={'via-sub-options-sidebar'}
|
||||
iconSuboptionStyles={'via-icons-sub-sidebar'}
|
||||
onClickLogout={() => doLogout()}
|
||||
setTitle={setTitle}
|
||||
userObj={userObj}
|
||||
environment={environment}
|
||||
presets={presets}
|
||||
router={router}
|
||||
i18n={i18n}
|
||||
/>
|
||||
}
|
||||
|
||||
{/* Content area */}
|
||||
<div className={`${sidebarOpen ? 'relative' : 'absolute'} flex h-screen w-full flex-1 flex-col overflow-y-auto overflow-x-hidden bg-gray-100 dark:bg-gray-700`}>
|
||||
{/* Site header */}
|
||||
{userObj && userObj.nombre_usuario &&
|
||||
<Navbar
|
||||
sidebarOpen={sidebarOpen}
|
||||
setSidebarOpen={setSidebarOpen}
|
||||
navbarStyles="via-navbar"
|
||||
menuButtonStyles="via-menu-btn-navbar"
|
||||
userMenuButtonStyles="via-user-menu-btn-navbar"
|
||||
userOptionStyles="via-user-options-navbar"
|
||||
userOptionSelectedStyles="via-user-options-selected-navbar"
|
||||
menuStyles="via-menu-navbar"
|
||||
onClickLogout={() => doLogout()}
|
||||
onClickProfile={() => router.push(`${presets.locations.profile}/${environment.getTime()}`)}
|
||||
MenuOptions={() => <UserOptionsMenu setPreferences={setPreferences} theme={theme} i18n={i18n} />}
|
||||
title={title}
|
||||
userObj={userObj}
|
||||
router={router}
|
||||
presets={presets}
|
||||
/>
|
||||
}
|
||||
|
||||
<main>
|
||||
<div className={userObj && userObj.nombre_usuario ? 'responsive-container' : ''}>
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{ userObj && userObj.nombre_usuario &&
|
||||
<SessionTimeout i18n={i18n} user={userObj} onTimeout={() => onTimeout()} onCancelTimeout={() => onCancelTimeout()} />
|
||||
}
|
||||
</div>
|
||||
<Footer footerStyle='via-footer' version={appVersion} label={i18n.t('common.version') || 'version'} />
|
||||
</div>
|
||||
</LayoutContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default ResponsiveContainer
|
13
src/lib/utils.js
Normal file
13
src/lib/utils.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export function cn (...inputs) {
|
||||
const classes = inputs
|
||||
.filter((input) => input)
|
||||
.flatMap((input) =>
|
||||
typeof input === 'object'
|
||||
? Object.entries(input).filter(([, value]) => value).map(([key]) => key)
|
||||
: input.split(' ')
|
||||
)
|
||||
|
||||
return twMerge(...classes)
|
||||
}
|
40
src/pages/_app.jsx
Normal file
40
src/pages/_app.jsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { RouteProvider } from '@/hooks/useRoute'
|
||||
import { useStore } from '@/hooks/useStore'
|
||||
import ResponsiveContainer from '@/layout/ResponsiveContainer'
|
||||
import LoadingProvider from '@/plugins/LoadingContext'
|
||||
import I18nProvider from '@/plugins/i18nContext'
|
||||
import Head from 'next/head'
|
||||
import { ToastContainer } from 'react-toastify'
|
||||
|
||||
import '@/styles/globals.css'
|
||||
import 'vComponents/styles/generated/bgColors.min.css'
|
||||
import 'vComponents/styles/generated/output.css'
|
||||
|
||||
export default function App ({ Component, pageProps }) {
|
||||
const [theme] = useStore(s => s.theme, a => a.setTheme)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="icon" href="https://wiki.via-asesores.com/logo-orbis_min.png" type="image/x-icon" />
|
||||
<title>Orbis Playground</title>
|
||||
</Head>
|
||||
|
||||
<I18nProvider locale={'es'} dict={''}>
|
||||
<LoadingProvider>
|
||||
<ResponsiveContainer>
|
||||
<ToastContainer
|
||||
theme={theme === 'dark' ? 'dark' : 'light'}
|
||||
toastClassName={theme === 'dark'
|
||||
? 'bg-black shadow-neon text-white rounded-lg'
|
||||
: 'bg-white text-black drop-shadow-lg rounded-lg shadow-lg'}
|
||||
/>
|
||||
<RouteProvider>
|
||||
<Component {...pageProps} />
|
||||
</RouteProvider>
|
||||
</ResponsiveContainer>
|
||||
</LoadingProvider>
|
||||
</I18nProvider>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
import '@/styles/globals.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
@ -2,7 +2,7 @@ import { Html, Head, Main, NextScript } from 'next/document'
|
||||
|
||||
export default function Document () {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Html lang="es">
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
@ -1,13 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
type Data = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
40
src/pages/dashboard/index.jsx
Normal file
40
src/pages/dashboard/index.jsx
Normal file
@ -0,0 +1,40 @@
|
||||
import presets from '@/utils/globalPresets'
|
||||
import React from 'react'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
const DashboardPage = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-4 gap-4 text-white">
|
||||
<div className='bg-theme-app-50' >01</div>
|
||||
<div className='bg-theme-app-100' >02</div>
|
||||
<div className='bg-theme-app-200' >03</div>
|
||||
<div className='bg-theme-app-300' >04</div>
|
||||
<div className='bg-theme-app-400' >05</div>
|
||||
<div className="col-span-3 grid grid-cols-subgrid gap-4 bg-theme-app-500">
|
||||
<div className="col-start-2">06</div>
|
||||
</div>
|
||||
<div className='bg-theme-app-600' >07</div>
|
||||
<div className='bg-theme-app-700' >08</div>
|
||||
<div className='bg-theme-app-800' >09</div>
|
||||
<div className='bg-theme-app-900' >10</div>
|
||||
<div className='bg-theme-app-950' >11</div>
|
||||
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="via-button shadow-neon bg-theme-app-500 backdrop:bg-gray-50"
|
||||
onClick={() => toast.success('Hola mundo', presets.toaster)}
|
||||
>
|
||||
Mostrar toast
|
||||
</button>
|
||||
|
||||
<button className="shadow-neon shadow-neon-sky h-8 w-44 rounded-full bg-red-600 text-center text-purple-200" >
|
||||
Button Neon
|
||||
</button>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default DashboardPage
|
115
src/pages/index.jsx
Normal file
115
src/pages/index.jsx
Normal file
@ -0,0 +1,115 @@
|
||||
import Image from 'next/image'
|
||||
import { useEffect } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { toast } from 'react-toastify'
|
||||
import md5 from 'js-md5'
|
||||
import { execute } from '@/helper/clientApi'
|
||||
import useI18n from '@/hooks/useI18n'
|
||||
import useLoading from '@/hooks/useLoading'
|
||||
import FormLogin from '@/components/Login/FormLogin'
|
||||
import presets from '@/utils/globalPresets'
|
||||
import environment from '@/utils/environment'
|
||||
import useGlobalContainer from '@/hooks/useGlobalContainer'
|
||||
import useHasMounted from '@/hooks/useHasMounted'
|
||||
|
||||
const LoginPage = () => {
|
||||
const global = useGlobalContainer()
|
||||
const router = useRouter()
|
||||
const i18n = useI18n()
|
||||
const loading = useLoading()
|
||||
const hasMounted = useHasMounted()
|
||||
const homePath = presets.locations.welcome
|
||||
|
||||
const loginUsuario = async (datos) => {
|
||||
loading.start()
|
||||
if (datos && datos !== null) {
|
||||
const hash = md5.create()
|
||||
hash.update(datos.password.trim())
|
||||
try {
|
||||
const resultado = await execute('SPR_LOGIN', [datos.email.toLowerCase().trim(), hash.hex()])
|
||||
loading.stop()
|
||||
if (resultado === undefined) {
|
||||
toast.error(i18n.t('common.errorApi'), presets.toaster)
|
||||
return
|
||||
}
|
||||
if (resultado[0].status === 'ERROR') {
|
||||
toast.error(resultado[0].message, presets.toaster)
|
||||
} else if (resultado[0].status === 'WARNING') {
|
||||
toast.warning(resultado[0].message, presets.toaster)
|
||||
} else if (resultado[0].token) {
|
||||
const user = {
|
||||
email: resultado[0].correo_electronico,
|
||||
nombre_usuario: resultado[0].nombre_completo,
|
||||
// img_user: resultado[0].foto,
|
||||
token: resultado[0].token,
|
||||
es_admin: resultado[0].es_admin,
|
||||
compania: resultado[0].nombre_compania,
|
||||
departamento: resultado[0].nombre_departamento,
|
||||
tiempo_expiracion: resultado[0].tiempo_expiracion,
|
||||
fecha_inicio_sesion: resultado[0].fecha_inicio_sesion,
|
||||
fecha_fin_sesion: resultado[0].fecha_fin_sesion,
|
||||
theme: resultado[0].theme,
|
||||
i18n: process.env.defaultLocale
|
||||
}
|
||||
const constante = {
|
||||
apptitle: process.env.apptitle,
|
||||
max_mb_upload: process.env.maxMbUpload
|
||||
}
|
||||
await environment.login(user.token, user, constante)
|
||||
await global.setTheme(user.theme)
|
||||
await global.setUserObj(user)
|
||||
await global.setConstanteObj(constante)
|
||||
await global.setToken(resultado[0].token)
|
||||
await i18n.locale(user.i18n)
|
||||
toast.success(`Bienvenido ${user.nombre_usuario}`, presets.toaster)
|
||||
router.push(homePath)
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(i18n.t('common.errorApi') + error, presets.toaster)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getEnv = async () => {
|
||||
const env = await environment.getEnvUser()
|
||||
if (env && env.token) {
|
||||
router.push(presets.locations.welcome)
|
||||
}
|
||||
}
|
||||
|
||||
// when page is mounted
|
||||
useEffect(() => {
|
||||
if (hasMounted) {
|
||||
getEnv()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hasMounted])
|
||||
|
||||
return (
|
||||
<div className={'via-login-page'}>
|
||||
<div className={'via-login-layout'}>
|
||||
{/* <!-- Tarjeta de Login --> */}
|
||||
<div className={'via-card'}>
|
||||
<div className={'via-login-header'}>
|
||||
<div className={'h-64 w-64 shrink-0'}>
|
||||
<Image src={presets.images.logo} alt="logo" className={'via-login-logo'} width={100} height={100} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="align-center flex w-full p-4 ">
|
||||
<div className={'via-login-form'}>
|
||||
{/* <!-- Formulario --> */}
|
||||
<FormLogin onLogin={loginUsuario} />
|
||||
{/* <!-- Fin Formulario --> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={'via-login-background'}
|
||||
style={{ backgroundImage: `url(${presets.images.loginFondo})`, minHeight: '75vh', backgroundSize: 'cover' }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginPage
|
@ -1,118 +0,0 @@
|
||||
import Image from 'next/image'
|
||||
import { Inter } from 'next/font/google'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main
|
||||
className={`flex min-h-screen flex-col items-center justify-between p-24 ${inter.className}`}
|
||||
>
|
||||
<div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
|
||||
<p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
|
||||
Get started by editing
|
||||
<code className="font-mono font-bold">src/pages/index.tsx</code>
|
||||
</p>
|
||||
<div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
|
||||
<a
|
||||
className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
By{' '}
|
||||
<Image
|
||||
src="/vercel.svg"
|
||||
alt="Vercel Logo"
|
||||
className="dark:invert"
|
||||
width={100}
|
||||
height={24}
|
||||
priority
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative flex place-items-center before:absolute before:h-[300px] before:w-[480px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-[240px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700/10 after:dark:from-sky-900 after:dark:via-[#0141ff]/40 before:lg:h-[360px]">
|
||||
<Image
|
||||
className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js Logo"
|
||||
width={180}
|
||||
height={37}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-4 lg:text-left">
|
||||
<a
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Docs{' '}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
Find in-depth information about Next.js features and API.
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Learn{' '}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
Learn about Next.js in an interactive course with quizzes!
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Templates{' '}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
Discover and deploy boilerplate example Next.js projects.
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Deploy{' '}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
Instantly deploy your Next.js site to a shareable URL with Vercel.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
9
src/pages/via-ui/[...index].jsx
Normal file
9
src/pages/via-ui/[...index].jsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const viaUIPage = () => {
|
||||
return (
|
||||
<div>viaUIPage</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default viaUIPage
|
29
src/pages/via-ui/tab/[...index].jsx
Normal file
29
src/pages/via-ui/tab/[...index].jsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React from 'react'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from 'via-ui/tab'
|
||||
|
||||
const TabPage = () => {
|
||||
const [tab, setTab] = React.useState('data')
|
||||
|
||||
return (
|
||||
<Tabs activeValue={tab}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="data" onClick={() => setTab('data')}>
|
||||
Datos del Evento
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="docs" onClick={() => setTab('docs')}>
|
||||
Documentos del Evento
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="data">
|
||||
tabs data
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="docs">
|
||||
tabs docs
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
|
||||
export default TabPage
|
56
src/pages/via-ui/test/[...index].jsx
Normal file
56
src/pages/via-ui/test/[...index].jsx
Normal file
@ -0,0 +1,56 @@
|
||||
import CodePlayground from '@/components/CodePlayground'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from 'via-ui/tab'
|
||||
|
||||
const initialCode =
|
||||
`// import { Tabs, TabsContent, TabsList, TabsTrigger } from 'via-ui/tab'
|
||||
|
||||
const TabsExample = () => {
|
||||
const [tab, setTab] = React.useState('data');
|
||||
return (
|
||||
<>
|
||||
<Tabs activeValue={tab} onChange={setTab}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="data">Datos del Evento</TabsTrigger>
|
||||
<TabsTrigger value="docs">Documentos del Evento</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="data">
|
||||
tabs data
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="docs">
|
||||
tabs docs
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
render(<TabsExample />);
|
||||
`
|
||||
|
||||
const ExampleComponentPage = () => {
|
||||
const [code, setCode] = useState(initialCode)
|
||||
|
||||
// El objeto scope debería incluir todos los componentes y funciones que el código JSX necesita
|
||||
const scope = {
|
||||
React,
|
||||
useState,
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='rounded-lg bg-white p-4 shadow-lg dark:!bg-[#222222]'>
|
||||
<CodePlayground code={code} onChange={(newCode) => { setCode(newCode) }} scope={scope} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ExampleComponentPage
|
26
src/plugins/LoadingContext.jsx
Normal file
26
src/plugins/LoadingContext.jsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { createContext, useState } from 'react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import presets from '@/utils/globalPresets'
|
||||
|
||||
const LoadingSpinner = dynamic(() => { return import('vComponents/dist/Loading') }, { ssr: false })
|
||||
export const LoadingContext = createContext()
|
||||
|
||||
const LoadingProvider = ({ children }) => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const loadingWrapper = {
|
||||
loading,
|
||||
setLoading,
|
||||
start: () => { setLoading(true) },
|
||||
stop: () => { setLoading(false) }
|
||||
}
|
||||
|
||||
return (
|
||||
<LoadingContext.Provider value={ loadingWrapper }>
|
||||
{ children }
|
||||
<LoadingSpinner loading={loading} image={presets.images.imageLoader} background={'backgroundLoader'} color={'colorLoader'} />
|
||||
</LoadingContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoadingProvider
|
81
src/plugins/i18nContext.jsx
Normal file
81
src/plugins/i18nContext.jsx
Normal file
@ -0,0 +1,81 @@
|
||||
import { createContext, useState, useRef, useEffect } from 'react'
|
||||
import rosetta from 'rosetta'
|
||||
import { execute } from '@/helper/clientApi'
|
||||
import useHasMounted from '@/hooks/useHasMounted'
|
||||
// import environment from '@/utils/environment'
|
||||
|
||||
const i18n = rosetta()
|
||||
const defaultLanguage = 'es'
|
||||
const languages = ['es', 'en']
|
||||
const contentLanguageMap = { es: 'es-GT', en: 'en-US' }
|
||||
const I18nContext = createContext()
|
||||
|
||||
// default language
|
||||
i18n.locale(defaultLanguage)
|
||||
|
||||
const I18nProvider = ({ children, locale, dict }) => {
|
||||
const activeLocaleRef = useRef(locale || defaultLanguage)
|
||||
const [, setTick] = useState(0)
|
||||
const firstRender = useRef(true)
|
||||
const [langIsLoaded, setLangIsLoaded] = useState(false)
|
||||
const hasMounted = useHasMounted()
|
||||
|
||||
const i18nWrapper = {
|
||||
activeLocale: activeLocaleRef.current,
|
||||
langIsLoaded,
|
||||
t: (...args) => i18n.t(...args),
|
||||
locale: (l, dict) => {
|
||||
i18n.locale(l)
|
||||
activeLocaleRef.current = l
|
||||
if (dict) {
|
||||
i18n.set(l, dict)
|
||||
}
|
||||
// force rerender to update view
|
||||
setTick((tick) => tick + 1)
|
||||
}
|
||||
}
|
||||
|
||||
const setLanguage = async () => {
|
||||
let msgs = {}
|
||||
// const env = await environment.getEnvUser()
|
||||
// const response = await execute('SPR_PROY_SESION_S', [env.token])
|
||||
// if (response[0].status !== 'ERROR') {
|
||||
// const options = await execute('SPR_CATALOGOS_S', ['idioma', env.token])
|
||||
// locale = options[0].idioma
|
||||
// } else {
|
||||
// locale = defaultLanguage
|
||||
// }
|
||||
locale = defaultLanguage
|
||||
// si la app ya esta renderizada se cargan los textos
|
||||
if (hasMounted) {
|
||||
const i18nDb = await execute('SPR_I18N_T', [locale])
|
||||
msgs = i18nDb.reduce((obj, elm) => {
|
||||
obj[elm.id_mensaje_padre] = { ...obj[elm.id_mensaje_padre], [elm.id_mensaje]: elm.mensaje }
|
||||
return obj
|
||||
}, {})
|
||||
}
|
||||
i18nWrapper.locale(locale, msgs)
|
||||
setLangIsLoaded(true)
|
||||
}
|
||||
|
||||
// for initial SSR render
|
||||
if (locale && firstRender.current === true) {
|
||||
firstRender.current = false
|
||||
setLanguage()
|
||||
}
|
||||
|
||||
// when page is mounted
|
||||
useEffect(() => {
|
||||
if (hasMounted) {
|
||||
setLanguage()
|
||||
}
|
||||
}, [hasMounted, activeLocaleRef.current])
|
||||
|
||||
return (
|
||||
<I18nContext.Provider value={i18nWrapper}>{children}</I18nContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export { defaultLanguage, languages, contentLanguageMap, I18nContext }
|
||||
|
||||
export default I18nProvider
|
58
src/styles/additional-styles/dropzone.css
Normal file
58
src/styles/additional-styles/dropzone.css
Normal file
@ -0,0 +1,58 @@
|
||||
@import "react-dropzone-component/styles/filepicker.css";
|
||||
|
||||
.dropzone.dz-clickable {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
height: 80px !important;
|
||||
min-height: 80px !important;
|
||||
margin: 0px !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-image {
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
width: 120px;
|
||||
height: 80px !important;
|
||||
position: relative;
|
||||
display: block;
|
||||
z-index: 10;
|
||||
margin: 0px !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-image img {
|
||||
display: block;
|
||||
max-height: 80px !important;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-details {
|
||||
z-index: 20;
|
||||
position: absolute;
|
||||
top: 8px !important;
|
||||
left: 0;
|
||||
margin: 0px !important;
|
||||
padding: 0px !important;
|
||||
opacity: 0;
|
||||
font-size: 13px;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
height: 80px !important;
|
||||
padding: 2em 1em;
|
||||
text-align: center;
|
||||
color: rgba(0,0,0,.9);
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
.filepicker-file-icon {
|
||||
margin: 5px !important;
|
||||
padding: 1px !important;
|
||||
}
|
7
src/styles/additional-styles/toastify.css
Normal file
7
src/styles/additional-styles/toastify.css
Normal file
@ -0,0 +1,7 @@
|
||||
@import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
.Toastify__toast-container--top-center {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
15
src/styles/component-styles/datatable.css
Normal file
15
src/styles/component-styles/datatable.css
Normal file
@ -0,0 +1,15 @@
|
||||
.via-states-datatable {
|
||||
@apply text-xs font-semibold mr-2 px-2.5 py-0.5 rounded;
|
||||
}
|
||||
.via-icon-active {
|
||||
@apply text-green-800 dark:text-green-300;
|
||||
}
|
||||
.via-icon-inactive {
|
||||
@apply text-red-800 dark:text-red-300;
|
||||
}
|
||||
.via-detalle-datatable {
|
||||
@apply text-white bg-blue-700 hover:bg-blue-800 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800;
|
||||
}
|
||||
.via-badge-datatable {
|
||||
@apply text-white bg-red-500 border-white dark:border-gray-900;
|
||||
}
|
112
src/styles/component-styles/loading.css
Normal file
112
src/styles/component-styles/loading.css
Normal file
@ -0,0 +1,112 @@
|
||||
.backgroundLoader {
|
||||
@apply bg-white backdrop-blur-md;
|
||||
}
|
||||
|
||||
.colorLoader {
|
||||
@apply bg-sky-500;
|
||||
}
|
||||
|
||||
.div_loading {
|
||||
@apply rounded-lg border border-gray-200 shadow-md sm:p-6 md:p-8 dark:border-gray-700 z-50;
|
||||
position: absolute;
|
||||
height: 35%;
|
||||
width: 30%;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
margin: 0 auto;
|
||||
width: 200px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.spinner > div {
|
||||
height: 100%;
|
||||
width: 8px;
|
||||
display: inline-block;
|
||||
margin-left: 3px;
|
||||
-webkit-animation: sk-stretchdelay 1.8s infinite ease-in-out;
|
||||
animation: sk-stretchdelay 1.8s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.spinner div:nth-child(1) {
|
||||
-webkit-animation-delay: -1.6s;
|
||||
animation-delay: -1.6s;
|
||||
}
|
||||
|
||||
.spinner div:nth-child(2) {
|
||||
-webkit-animation-delay: -1.4s;
|
||||
animation-delay: -1.4s;
|
||||
}
|
||||
|
||||
.spinner div:nth-child(3) {
|
||||
-webkit-animation-delay: -1.2s;
|
||||
animation-delay: -1.2s;
|
||||
}
|
||||
|
||||
.spinner div:nth-child(4) {
|
||||
-webkit-animation-delay: -1.0s;
|
||||
animation-delay: -1.0s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-stretchdelay {
|
||||
0%, 40%, 100% { -webkit-transform: scaleY(0.4) }
|
||||
20% { -webkit-transform: scaleY(1.0) }
|
||||
}
|
||||
|
||||
@keyframes sk-stretchdelay {
|
||||
0%, 40%, 100% {
|
||||
transform: scaleY(0.4);
|
||||
-webkit-transform: scaleY(0.4);
|
||||
} 20% {
|
||||
transform: scaleY(1.0);
|
||||
-webkit-transform: scaleY(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
.lds-facebook {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.lds-facebook div {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
width: 16px;
|
||||
background: #fff;
|
||||
animation: lds-facebook 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite;
|
||||
}
|
||||
|
||||
.lds-facebook div:nth-child(1) {
|
||||
left: 8px;
|
||||
animation-delay: -0.24s;
|
||||
}
|
||||
|
||||
.lds-facebook div:nth-child(2) {
|
||||
left: 32px;
|
||||
animation-delay: -0.12s;
|
||||
}
|
||||
|
||||
.lds-facebook div:nth-child(3) {
|
||||
left: 56px;
|
||||
animation-delay: 0;
|
||||
}
|
||||
|
||||
@keyframes lds-facebook {
|
||||
0% {
|
||||
top: 8px;
|
||||
height: 64px;
|
||||
}
|
||||
50%, 100% {
|
||||
top: 24px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
27
src/styles/component-styles/navbar.css
Normal file
27
src/styles/component-styles/navbar.css
Normal file
@ -0,0 +1,27 @@
|
||||
.via-navbar {
|
||||
@apply bg-white dark:bg-[#222222] border-slate-200 text-theme-app-500 dark:text-theme-app-50 font-semibold transition-colors duration-[350ms] ease-in-out !important;
|
||||
}
|
||||
|
||||
.via-menu-btn-navbar {
|
||||
@apply text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 transition-colors duration-[350ms] ease-in-out shadow-neon rounded-md !important;
|
||||
}
|
||||
|
||||
.via-user-menu-btn-navbar {
|
||||
@apply bg-slate-700 text-white transition-colors duration-[350ms] ease-in-out !important;
|
||||
}
|
||||
|
||||
.via-user-options-navbar {
|
||||
@apply bg-transparent border border-transparent dark:border-theme-app-400 text-gray-900 dark:text-white transition-colors duration-[350ms] ease-in-out !important;
|
||||
}
|
||||
|
||||
.via-user-options-selected-navbar {
|
||||
@apply bg-theme-app-50 border border-transparent dark:bg-theme-app-500 transition-colors duration-[350ms] ease-in-out !important;
|
||||
}
|
||||
|
||||
.via-menu-navbar {
|
||||
@apply bg-white dark:bg-[#222222] border-slate-200 text-black dark:text-white transition-colors duration-[350ms] ease-in-out !important;
|
||||
}
|
||||
|
||||
.via-footer {
|
||||
@apply bg-white dark:bg-[#222222] border-slate-200 text-black dark:text-white transition-colors duration-[350ms] ease-in-out text-xs !h-8 !important;
|
||||
}
|
40
src/styles/component-styles/sidebar.css
Normal file
40
src/styles/component-styles/sidebar.css
Normal file
@ -0,0 +1,40 @@
|
||||
.via-bg-sidebar {
|
||||
@apply bg-theme-app-500 border border-theme-app-300 rounded-t-lg md:rounded-t-none md:rounded-b-lg md:rounded-none dark:border-theme-app-800 dark:from-theme-app-700 dark:to-theme-app-800 !important;
|
||||
}
|
||||
|
||||
.via-options-sidebar {
|
||||
@apply text-theme-app-50 bg-transparent transition-colors duration-150 font-semibold hover:bg-theme-app-50 hover:text-gray-800 dark:text-theme-app-50 dark:hover:bg-theme-app-200 first-letter:uppercase !important;
|
||||
}
|
||||
|
||||
.via-sub-options-sidebar {
|
||||
@apply text-white hover:bg-theme-app-100 transition-colors duration-150 font-semibold gap-x-1 hover:text-gray-800 dark:text-theme-app-50 dark:hover:bg-theme-app-300 !important;
|
||||
}
|
||||
|
||||
.via-drawer-count {
|
||||
@apply inline-flex items-center w-8 justify-center mr-1 px-2 py-2 text-sm font-semibold leading-none bg-teal-800 text-white rounded-full
|
||||
}
|
||||
|
||||
.via-app-title-sidebar {
|
||||
@apply !text-theme-app-50 dark:text-theme-app-50 hover:bg-theme-app-800 dark:hover:bg-theme-app-950 !rounded-lg px-1 cursor-default !important;
|
||||
}
|
||||
|
||||
.via-user-info-sidebar {
|
||||
@apply !text-white bg-theme-app-700 dark:bg-theme-app-900 hover:bg-theme-app-800 dark:hover:bg-theme-app-950 !rounded-t-lg py-1 cursor-default;
|
||||
}
|
||||
|
||||
.via-icons-sidebar {
|
||||
@apply !text-theme-app-950 bg-blue-100 rounded-md border p-[1px] drop-shadow-md !important;
|
||||
}
|
||||
|
||||
.via-icons-sub-sidebar {
|
||||
@apply !text-theme-app-950 bg-cyan-50 rounded-md h-6 w-6 p-[1px] drop-shadow-md !important;
|
||||
}
|
||||
|
||||
.via-input-search-sidebar {
|
||||
@apply w-full rounded-l-lg shadow-lg !border-l-2 !border-y-2 border-r-0 border-[#7b1fa2] focus:border-blue-500 !important;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5) !important;
|
||||
}
|
||||
|
||||
.via-button-search-sidebar {
|
||||
@apply bg-theme-app-500 hover:bg-theme-app-600 text-white flex justify-center items-center p-2 rounded-r-lg cursor-pointer shadow-neon dark:!border-l-0 !important;
|
||||
}
|
@ -1,27 +1,91 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=fallback');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Material+Icons+Round');
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-start-rgb: 214, 219, 220;
|
||||
--background-end-rgb: 255, 255, 255;
|
||||
/* ===== Scrollbar CSS ===== */
|
||||
@layer utilities {
|
||||
/* Firefox */
|
||||
* {
|
||||
scrollbar-width: auto;
|
||||
scrollbar-color: #1F92BF #e1e1e1;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
/* Chrome, Edge, and Safari */
|
||||
*::-webkit-scrollbar {
|
||||
@apply w-2 h-2 bg-gray-200 dark:bg-gray-800 rounded-lg;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
@apply bg-theme-app-600 rounded-lg border-theme-app-400 dark:bg-theme-app-600 dark:border-theme-app-600;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-theme-app-700 border-theme-app-700 dark:bg-theme-app-900 dark:border-theme-app-900;
|
||||
transition: 0s;
|
||||
}
|
||||
|
||||
@keyframes pulse-neon {
|
||||
0%, 100% {
|
||||
box-shadow:
|
||||
0 0 4px var(--tw-shadow-color),
|
||||
0 0 8px var(--tw-shadow-color);
|
||||
}
|
||||
25% {
|
||||
box-shadow:
|
||||
0 0 8px var(--tw-shadow-color),
|
||||
0 0 10px var(--tw-shadow-color);
|
||||
}
|
||||
50% {
|
||||
box-shadow:
|
||||
0 0 6px var(--tw-shadow-color),
|
||||
0 0 12px var(--tw-shadow-color);
|
||||
}
|
||||
75% {
|
||||
box-shadow:
|
||||
0 0 8px var(--tw-shadow-color),
|
||||
0 0 14px var(--tw-shadow-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgb(var(--background-end-rgb))
|
||||
)
|
||||
rgb(var(--background-start-rgb));
|
||||
@layer components {
|
||||
.shadow-neon {
|
||||
@apply shadow-lg;
|
||||
border: 2px solid var(--tw-border-color);
|
||||
--tw-shadow-color: rgba(0, 0, 0, 0.25);
|
||||
--tw-border-color: transparent;
|
||||
box-shadow: 0 2px 15px var(--tw-shadow-color);
|
||||
}
|
||||
|
||||
.dark .shadow-neon {
|
||||
--tw-shadow-color: #9c27b0;
|
||||
--tw-border-color: #7b1fa2;
|
||||
box-shadow: 0 2px 15px var(--tw-shadow-color);
|
||||
}
|
||||
.dark .shadow-neon:hover {
|
||||
animation: pulse-neon 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.dark .shadow-neon.shadow-neon-purple {
|
||||
--tw-shadow-color: #9c27b0;
|
||||
--tw-border-color: #7b1fa2;
|
||||
}
|
||||
|
||||
.dark .shadow-neon.shadow-neon-sky {
|
||||
--tw-shadow-color: #00c4e2;
|
||||
--tw-border-color: #00a2ba;
|
||||
}
|
||||
}
|
||||
|
||||
/* Additional Styles */
|
||||
@import 'additional-styles/toastify.css';
|
||||
@import 'component-styles/loading.css';
|
||||
@import 'component-styles/sidebar.css';
|
||||
@import 'component-styles/navbar.css';
|
||||
@import 'component-styles/datatable.css';
|
||||
@import 'page-styles/common.css';
|
||||
@import 'page-styles/login.css';
|
||||
@import 'ui-styles/via-ui.css';
|
150
src/styles/page-styles/common.css
Normal file
150
src/styles/page-styles/common.css
Normal file
@ -0,0 +1,150 @@
|
||||
.via-card {
|
||||
@apply block rounded-md shadow-md w-full border-gray-300 border drop-shadow-lg bg-white dark:bg-[#4E4E4E] shadow-[#D1D1D1] hover:shadow-[#00000033];
|
||||
}
|
||||
|
||||
.via-label {
|
||||
@apply block text-gray-500 dark:text-gray-300 text-[14px] xl:text-[15px] font-medium mb-1 pl-0;
|
||||
}
|
||||
|
||||
.via-input {
|
||||
@apply w-full text-sm text-gray-900 dark:text-neutral-900 rounded-lg border border-gray-300 focus:ring-stone-500 focus:border-stone-500 dark:border-stone-600 dark:placeholder-gray-400 dark:focus:border-stone-500 !important;
|
||||
}
|
||||
|
||||
.via-input-prepend {
|
||||
@apply w-full text-sm text-gray-900 dark:text-neutral-900 rounded-l-lg border-l-gray-100 border border-gray-300 focus:ring-stone-500 focus:border-stone-500 dark:border-stone-600 dark:placeholder-gray-400 dark:focus:border-stone-500 !important;
|
||||
}
|
||||
|
||||
.via-append-input {
|
||||
@apply absolute top-0 -right-1 h-full p-1 text-sm font-medium text-white bg-theme-app-500 dark:bg-theme-app-900 rounded-r-lg border border-theme-app-900 hover:bg-theme-app-600 focus:ring-4 focus:outline-none focus:ring-theme-app-300 dark:hover:bg-theme-app-900 dark:focus:ring-theme-app-800;
|
||||
}
|
||||
|
||||
.via-append-input-extra {
|
||||
@apply absolute top-0 right-0 h-full p-1 mr-6 text-sm font-medium text-white bg-theme-app-500 dark:bg-theme-app-900 border border-theme-app-900 hover:bg-theme-app-800 focus:ring-4 focus:outline-none focus:ring-theme-app-300 dark:hover:bg-theme-app-900 dark:focus:ring-theme-app-800;
|
||||
}
|
||||
|
||||
.via-button {
|
||||
@apply cursor-pointer inline-flex items-center h-8 px-2 m-1 text-white ease-linear transition-colors duration-150 rounded-md border border-gray-300 dark:border-gray-700;
|
||||
}
|
||||
.via-div-actions {
|
||||
@apply flex w-full justify-center flex-wrap;
|
||||
}
|
||||
|
||||
.via-no {
|
||||
@apply text-red-700 dark:text-red-600;
|
||||
}
|
||||
|
||||
.via-yes {
|
||||
@apply text-green-700 dark:text-green-600;
|
||||
}
|
||||
|
||||
.via-default {
|
||||
@apply text-theme-app-700 dark:text-theme-app-300;
|
||||
}
|
||||
|
||||
.via-default-bg {
|
||||
@apply bg-theme-app-700 dark:bg-theme-app-300;
|
||||
}
|
||||
|
||||
.via-default-active {
|
||||
@apply text-theme-app-500 dark:text-theme-app-500;
|
||||
}
|
||||
|
||||
.via-yellow {
|
||||
@apply text-amber-400 dark:text-amber-300 ;
|
||||
}
|
||||
|
||||
.via-yellow-bg {
|
||||
@apply bg-amber-500 dark:bg-amber-600 ;
|
||||
}
|
||||
|
||||
.via-yellow-active {
|
||||
@apply text-amber-500 dark:text-amber-600 ;
|
||||
}
|
||||
|
||||
.via-green {
|
||||
@apply text-green-400 dark:text-green-300;
|
||||
}
|
||||
|
||||
.via-green-bg {
|
||||
@apply bg-green-500 dark:bg-green-600;
|
||||
}
|
||||
|
||||
.via-green-active {
|
||||
@apply text-green-600 dark:text-green-600;
|
||||
}
|
||||
|
||||
.via-red {
|
||||
@apply text-red-400 dark:text-red-300;
|
||||
}
|
||||
|
||||
.via-red-bg {
|
||||
@apply bg-red-500 dark:bg-red-600;
|
||||
}
|
||||
|
||||
.via-red-active {
|
||||
@apply text-red-600 dark:text-red-600;
|
||||
}
|
||||
|
||||
.via-blue {
|
||||
@apply text-blue-400 dark:text-blue-300;
|
||||
}
|
||||
|
||||
.via-blue-bg {
|
||||
@apply bg-blue-500 dark:bg-blue-600;
|
||||
}
|
||||
|
||||
.via-blue-active {
|
||||
@apply text-blue-600 dark:text-blue-600;
|
||||
}
|
||||
|
||||
.via-translate {
|
||||
@apply -translate-x-1/2;
|
||||
}
|
||||
|
||||
.via-opacity {
|
||||
@apply bg-opacity-25;
|
||||
}
|
||||
|
||||
.via-img-preview {
|
||||
@apply h-16 max-h-16 !important;
|
||||
}
|
||||
|
||||
.responsive-container {
|
||||
@apply w-full h-auto min-h-[calc(100vh-60px)] mx-auto bg-[#ECEFF8] dark:bg-[#0f0f0f] items-center pb-10 p-2 transition-colors duration-[350ms] ease-in-out text-[#0F0F0F] dark:text-[#F1F1F1] !important;
|
||||
}
|
||||
|
||||
.via-title {
|
||||
@apply font-semibold text-[#0F0F0F] dark:text-[#F1F1F1]
|
||||
}
|
||||
|
||||
.via-subtitle {
|
||||
@apply text-[#606060] dark:text-[#AAAAAA]
|
||||
}
|
||||
|
||||
.via-text {
|
||||
@apply text-[#131313] dark:text-[#FFFFFF]
|
||||
}
|
||||
|
||||
/* Clase para el color warning (azul) */
|
||||
.via-warning {
|
||||
@apply bg-blue-500 hover:bg-blue-600 dark:bg-warning-dark dark:hover:bg-warning-hover-dark text-white !important;
|
||||
}
|
||||
|
||||
/* Clase para el color danger (rojo) */
|
||||
.via-danger {
|
||||
@apply bg-danger hover:bg-danger-hover dark:bg-danger-dark dark:hover:bg-danger-hover-dark text-white !important;
|
||||
}
|
||||
|
||||
/* Clase para el color success (verde) */
|
||||
.via-success {
|
||||
@apply bg-success hover:bg-success-hover dark:bg-success-dark dark:hover:bg-success-hover-dark text-white !important;
|
||||
}
|
||||
|
||||
|
||||
.via-background {
|
||||
@apply bg-[#F6F6F6] dark:bg-[#272727] !important;
|
||||
}
|
||||
|
||||
/* .test {
|
||||
@apply bg-theme-app-
|
||||
} */
|
23
src/styles/page-styles/login.css
Normal file
23
src/styles/page-styles/login.css
Normal file
@ -0,0 +1,23 @@
|
||||
.via-login-page {
|
||||
@apply flex flex-wrap w-screen h-screen bg-gray-200;
|
||||
}
|
||||
|
||||
.via-login-layout {
|
||||
@apply flex flex-shrink w-full h-full md:w-1/2 lg:w-1/3 bg-white;
|
||||
}
|
||||
|
||||
.via-login-header {
|
||||
@apply flex w-full p-4 justify-center overflow-hidden;
|
||||
}
|
||||
|
||||
.via-login-logo {
|
||||
@apply w-64 h-64 bg-center bg-auto bg-no-repeat object-contain !important;
|
||||
}
|
||||
|
||||
.via-login-background {
|
||||
@apply hidden flex-shrink h-full md:w-1/2 lg:w-2/3 md:flex bg-center bg-contain;
|
||||
}
|
||||
|
||||
.via-login-form {
|
||||
@apply flex w-full flex-col space-y-4;
|
||||
}
|
214
src/styles/ui-styles/via-ui.css
Normal file
214
src/styles/ui-styles/via-ui.css
Normal file
@ -0,0 +1,214 @@
|
||||
.main-container {
|
||||
@apply grid gap-x-5 gap-y-3 2xl:gap-y-8 my-2 2xl:my-4 text-slate-700 justify-center content-around;
|
||||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||||
}
|
||||
|
||||
.card {
|
||||
@apply flex flex-col w-full rounded-lg border duration-300 items-center bg-white dark:bg-[#272727] shadow-[#D1D1D1] hover:shadow-[#00000033];
|
||||
}
|
||||
|
||||
.card-header {
|
||||
@apply flex flex-col w-full rounded-t-lg border-b duration-300 items-center bg-white dark:bg-[#272727] shadow-[#D1D1D1] hover:shadow-[#00000033];
|
||||
}
|
||||
|
||||
.header-card {
|
||||
@apply bg-gradient-to-br dark:bg-gradient-to-br from-theme-app-600 to-theme-app-400 dark:from-theme-app-800 dark:to-theme-app-600 flex h-12 w-full items-center justify-center rounded-t-lg;
|
||||
}
|
||||
|
||||
.header-items {
|
||||
@apply grid grid-cols-2 justify-start text-white font-medium gap-2 items-center w-full py-2 ;
|
||||
grid-template-columns: auto auto;
|
||||
}
|
||||
|
||||
.text-header {
|
||||
@apply font-semibold md:ml-2 ml-1 text-white justify-start left-0 ;
|
||||
overflow:hidden;
|
||||
height: auto;
|
||||
-webkit-box-orient: vertical;
|
||||
display: block;
|
||||
display: -webkit-box;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-loading {
|
||||
animation: loading 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
/* tooltip styles */
|
||||
|
||||
.tooltip-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tooltip-box {
|
||||
@apply !absolute;
|
||||
background-color: #555;
|
||||
color: #fff;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.tooltip-top {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.tooltip-bottom {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.tooltip-left {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.tooltip-right {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* tabs styles */
|
||||
.tabs {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.tabsContentWrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tabsContent {
|
||||
/* position: absolute; */
|
||||
transition: transform 0.6s ease-in-out;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tabsContent {
|
||||
@apply w-full;
|
||||
transition: transform 0.6s ease-in-out, opacity 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
.tabsContent[data-active="false"] {
|
||||
transform: translateX(-100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tabsContent[data-active="true"] {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.tabsList {
|
||||
@apply flex w-full rounded-t-lg border-b duration-300 items-center bg-white dark:bg-[#272727] shadow-[#D1D1D1] hover:shadow-[#00000033] p-1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.tabsTrigger {
|
||||
@apply bg-white dark:bg-[#3D3D3D] text-gray-500 dark:text-gray-200 hover:text-gray-700 dark:hover:text-gray-300 active:!bg-gray-100 dark:active:!bg-[#3D3D3D] font-medium transition-colors duration-500 delay-1000 ease-in-out;
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
transition: background-color 0.75s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tabsTrigger[data-active="true"] {
|
||||
@apply bg-white text-theme-app-500 dark:text-theme-app-900 font-bold hover:font-extrabold;
|
||||
}
|
||||
|
||||
.tabsIndicator {
|
||||
@apply bg-theme-app-500 dark:bg-theme-app-900;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 4px;
|
||||
transition: left 0.75s ease, right 0.75s ease;
|
||||
}
|
||||
|
||||
.slide-left-in {
|
||||
animation: slide-left-in 0.75s forwards;
|
||||
}
|
||||
|
||||
.slide-right-in {
|
||||
animation: slide-right-in 0.75s forwards;
|
||||
}
|
||||
|
||||
.slide-left-out {
|
||||
animation: slide-left-out 0.75s forwards;
|
||||
}
|
||||
|
||||
.slide-right-out {
|
||||
animation: slide-right-out 0.75s forwards;
|
||||
}
|
||||
|
||||
@keyframes slide-left-in {
|
||||
from { transform: translateX(100%); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
|
||||
@keyframes slide-right-in {
|
||||
from { transform: translateX(-100%); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
|
||||
@keyframes slide-left-out {
|
||||
from { transform: translateX(0); }
|
||||
to { transform: translateX(-100%); }
|
||||
}
|
||||
|
||||
@keyframes slide-right-out {
|
||||
from { transform: translateX(0); }
|
||||
to { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
/* checkbox styles */
|
||||
|
||||
|
||||
.filter-button{
|
||||
@apply items-center hover:bg-theme-app-400 dark:hover:bg-theme-app-700 rounded-lg grid grid-cols-2 h-10 px-4 gap-2 text-slate-700 hover:text-white dark:text-white font-medium duration-300 dark:hover:text-white;
|
||||
grid-template-columns: auto auto;
|
||||
}
|
||||
|
||||
|
||||
.Accordion > .AccordionTrigger:first-child > div:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.Accordion > .AccordionTrigger:last-child > div:last-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.tooltip-indicator {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 8px solid gray;
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 50;
|
||||
}
|
365
src/utils/environment.js
Normal file
365
src/utils/environment.js
Normal file
@ -0,0 +1,365 @@
|
||||
import Cookie from 'js-cookie'
|
||||
import functions from 'v-functions'
|
||||
import presets from './globalPresets'
|
||||
import { findWhere } from 'underscore'
|
||||
import { execute } from '@/helper/clientApi'
|
||||
|
||||
/**
|
||||
* @typedef {Object} Environment
|
||||
* @property {function} getResultParameters - Funcion para obtener los parametros de un resultado
|
||||
* @property {function} buildFunctiontsHeaders - Funcion para construir las funciones de los headers
|
||||
* @property {function} setEnvUser - Funcion para guardar el usuario en el localstorage
|
||||
* @property {function} getEnvUser - Funcion para obtener el usuario del localstorage
|
||||
* @property {function} logout - Funcion para cerrar sesion
|
||||
* @property {function} login - Funcion para iniciar sesion
|
||||
* @property {function} getTime - Funcion para obtener la hora actual
|
||||
* @property {function} validaResultadoDB - Funcion para validar el resultado de una peticion a la base de datos
|
||||
* @property {function} getFileParams - Funcion para obtener los parametros de un archivo
|
||||
* @property {function} validaPermisos - Funcion para validar los permisos de un usuario
|
||||
* @property {function} i18nHeaders - Funcion para traducir los headers
|
||||
*
|
||||
* @type {Environment}
|
||||
* @exports environment
|
||||
* @description Este archivo contiene las funciones globales de la aplicacion
|
||||
* @example
|
||||
* import environment from '@/utils/environment'
|
||||
*
|
||||
* environment.getResultParameters(confParameters, item, mainParameters)
|
||||
*
|
||||
*/
|
||||
const environment = {
|
||||
getResultParameters: (confParameters, item, mainParameters) => {
|
||||
const resultParameters = []
|
||||
if (confParameters != null && confParameters.length) {
|
||||
confParameters.forEach((orParam) => {
|
||||
if (orParam.type === 'param') {
|
||||
const _parameters = [...mainParameters]
|
||||
const arrParam = _parameters.filter((paramSel) => (paramSel.name === orParam.id))
|
||||
if (arrParam.length) {
|
||||
resultParameters.push(arrParam[0].value)
|
||||
} else {
|
||||
resultParameters.push(null)
|
||||
}
|
||||
} else {
|
||||
if (item != null && item[orParam.id] != null) {
|
||||
resultParameters.push(item[orParam.id])
|
||||
} else {
|
||||
resultParameters.push(null)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return resultParameters
|
||||
},
|
||||
buildFunctiontsHeaders: (baseHeaders, iconResponse = () => {}, substituteValOnForm) => {
|
||||
const newHeaders = []
|
||||
if (baseHeaders != null && baseHeaders.length) {
|
||||
for (let i = 0; i < baseHeaders.length; i++) {
|
||||
const field = baseHeaders[i]
|
||||
if (field.substituteValOnForm != null && substituteValOnForm != null) {
|
||||
field.substituteValOnForm = (head, _model, _field, _isEdit) => { return substituteValOnForm(head, _model, _field, _isEdit) }
|
||||
}
|
||||
if (field.mappingRules != null && field.mappingRules.length) {
|
||||
const _mappingRules = [...field.mappingRules]
|
||||
// Insertar Funcion para substituteVal
|
||||
const rulesValue = _mappingRules.filter((rule) => (rule.type === 'value' || rule.type === 'icon' || rule.type === 'format' || rule.type === 'fieldValue'))
|
||||
if (rulesValue.length) {
|
||||
field.substituteVal = (item) => {
|
||||
const id = field.value
|
||||
const rules = [...rulesValue]
|
||||
if (rules.length) {
|
||||
for (let i = 0; i < rules.length; i++) {
|
||||
const rule = { ...rules[i] }
|
||||
const numberRuleValue = Number(rule.value)
|
||||
const numberFieldValue = Number(item[rule.field])
|
||||
const areNums = !isNaN(numberRuleValue) && !isNaN(numberFieldValue)
|
||||
let response = rule.result
|
||||
if (rule.type === 'icon') {
|
||||
response = iconResponse(item[id], rule.result, item)
|
||||
}
|
||||
if (rule.rule === 'equal') {
|
||||
if ((!areNums && item[rule.field] === rule.value) || (areNums && numberFieldValue === numberRuleValue)) {
|
||||
return response
|
||||
} else if ((rule.value === null || rule.value === '') && (item[rule.field] === null || item[rule.field] === '')) {
|
||||
return response
|
||||
}
|
||||
} else if (rule.rule === 'greater_than') {
|
||||
if ((!areNums && item[rule.field] > rule.value) || (areNums && numberFieldValue > numberRuleValue)) {
|
||||
return response
|
||||
}
|
||||
} else if (rule.rule === 'less_than') {
|
||||
if ((!areNums && item[rule.field] < rule.value) || (areNums && numberFieldValue < numberRuleValue)) {
|
||||
return response
|
||||
}
|
||||
} else if (rule.rule === 'not_equal') {
|
||||
if ((!areNums && item[rule.field] !== rule.value) || (areNums && numberFieldValue !== numberRuleValue)) {
|
||||
return response
|
||||
} else if ((rule.value === null || rule.value === '') && (item[rule.field] !== null || item[rule.field] !== '')) {
|
||||
return response
|
||||
}
|
||||
}
|
||||
if (rule.type === 'format') {
|
||||
const isNum = !isNaN(numberFieldValue)
|
||||
try {
|
||||
if (rule.rule === 'number') {
|
||||
if (isNum) {
|
||||
return functions.formatNumber(numberFieldValue, rule.result)
|
||||
}
|
||||
return functions.formatNumber(item[rule.field], rule.result)
|
||||
} else if (rule.rule === 'date') {
|
||||
if (isNum) {
|
||||
return functions.formatDate(numberFieldValue, rule.result)
|
||||
}
|
||||
return functions.formatDate(item[rule.field], rule.result)
|
||||
}
|
||||
} catch (e) {
|
||||
return item[id]
|
||||
}
|
||||
}
|
||||
if (rule.type === 'fieldValue') {
|
||||
try {
|
||||
return item[rule.result]
|
||||
} catch (e) {
|
||||
return item[id]
|
||||
}
|
||||
}
|
||||
}
|
||||
return item[id]
|
||||
}
|
||||
return item[id]
|
||||
}
|
||||
} else {
|
||||
field.substituteVal = field.substituteVal || null
|
||||
}
|
||||
// Insertar Funcion para classItem
|
||||
const rulesClass = _mappingRules.filter((rule) => (rule.type === 'class'))
|
||||
if (rulesClass.length) {
|
||||
field.classItem = (item) => {
|
||||
const rules = [...rulesClass]
|
||||
let resultClass = ''
|
||||
if (rules.length) {
|
||||
for (let i = 0; i < rules.length; i++) {
|
||||
const rule = { ...rules[i] }
|
||||
const numberRuleValue = Number(rule.value)
|
||||
const numberFieldValue = Number(item[rule.field])
|
||||
const areNums = !isNaN(numberRuleValue) && !isNaN(numberFieldValue)
|
||||
if (rule.rule === 'equal') {
|
||||
if ((!areNums && item[rule.field] === rule.value) || (areNums && numberFieldValue === numberRuleValue)) {
|
||||
resultClass = `${resultClass} ${rule.result}`
|
||||
} else if ((rule.value === null || rule.value === '') && (item[rule.field] === null || item[rule.field] === '')) {
|
||||
resultClass = `${resultClass} ${rule.result}`
|
||||
}
|
||||
} else if (rule.rule === 'greater_than') {
|
||||
if ((!areNums && item[rule.field] > rule.value) || (areNums && numberFieldValue > numberRuleValue)) {
|
||||
resultClass = `${resultClass} ${rule.result}`
|
||||
}
|
||||
} else if (rule.rule === 'less_than') {
|
||||
if ((!areNums && item[rule.field] < rule.value) || (areNums && numberFieldValue < numberRuleValue)) {
|
||||
resultClass = `${resultClass} ${rule.result}`
|
||||
}
|
||||
} else if (rule.rule === 'not_equal') {
|
||||
if ((!areNums && item[rule.field] !== rule.value) || (areNums && numberFieldValue !== numberRuleValue)) {
|
||||
resultClass = `${resultClass} ${rule.result}`
|
||||
} else if ((rule.value === null || rule.value === '') && (item[rule.field] !== null || item[rule.field] !== '')) {
|
||||
resultClass = `${resultClass} ${rule.result}`
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultClass
|
||||
}
|
||||
return resultClass
|
||||
}
|
||||
} else {
|
||||
field.classItem = null
|
||||
}
|
||||
} else {
|
||||
field.classItem = null
|
||||
field.substituteVal = field.substituteVal || null
|
||||
}
|
||||
|
||||
if (field.showInForm != null && field.showInForm === true) {
|
||||
if (field.formatDefinition != null && field.formatDefinition.action != null) {
|
||||
if (field.formatDefinition.action === 'format') {
|
||||
field.format = (val, definition = field.formatDefinition) => {
|
||||
if (val != null) {
|
||||
if (definition.value === 'lowercase') {
|
||||
const valString = String(val)
|
||||
return valString.toLowerCase()
|
||||
} else if (definition.value === 'uppercase') {
|
||||
const valString = String(val)
|
||||
return valString.toUpperCase()
|
||||
}
|
||||
}
|
||||
return val
|
||||
}
|
||||
}
|
||||
} else {
|
||||
field.format = null
|
||||
}
|
||||
}
|
||||
newHeaders.push({ ...field })
|
||||
}
|
||||
}
|
||||
return newHeaders
|
||||
},
|
||||
setEnvUser: (user) => {
|
||||
functions.setEncodeStorage(`${process.env.idApp}/${process.env.NODE_ENV}`, user)
|
||||
},
|
||||
getEnvUser: () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const token = Cookie.get(`${process.env.idApp}:${process.env.NODE_ENV}`)
|
||||
const user = functions.getDecodeStorage(`${process.env.idApp}/${process.env.NODE_ENV}`)
|
||||
if (!token || !user) {
|
||||
resolve({ token: undefined, user: undefined, redirectPath: '/login' })
|
||||
}
|
||||
resolve({ token, user })
|
||||
})
|
||||
},
|
||||
logout: () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
Cookie.remove(`${process.env.idApp}:${process.env.NODE_ENV}`)
|
||||
functions.setEncodeStorage(`${process.env.idApp}/${process.env.NODE_ENV}`, '')
|
||||
resolve('/')
|
||||
})
|
||||
},
|
||||
login: (token, user) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
Cookie.set(`${process.env.idApp}:${process.env.NODE_ENV}`, token)
|
||||
functions.setEncodeStorage(`${process.env.idApp}/${process.env.NODE_ENV}`, user)
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
getTime: () => {
|
||||
// return (new Date()).getTime()
|
||||
return 1668220842700
|
||||
},
|
||||
validaResultadoDB: (resultado, i18n, toast, showOKMessage = false) => {
|
||||
const showToast = (i18n, toast, message, type, i18nCode = null) => {
|
||||
const displayMessage = message
|
||||
|
||||
// if (displayMessage.includes(':console-message:')) {
|
||||
// console.warn(displayMessage.replace(':console-message:', ''))
|
||||
// } else
|
||||
if (toast && showOKMessage) {
|
||||
const cleanedMessage = displayMessage.replace(':no-display:', '')
|
||||
toast[type](
|
||||
<span dangerouslySetInnerHTML={{ __html: cleanedMessage }} />,
|
||||
presets.toaster
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (!resultado) {
|
||||
showToast(i18n, toast, i18n.t('common.errorApi'), 'error')
|
||||
return false
|
||||
}
|
||||
|
||||
if (Array.isArray(resultado) && resultado.length > 0) {
|
||||
const { status, message, mensaje, i18n: i18nCode } = resultado[0]
|
||||
|
||||
if (status === 'ERROR' || status === 'error' || status === 'WARNING' || status === 'warning' || status === 'INFO' || status === 'info') {
|
||||
if (i18n === 'session.inactive' || i18n === 'session.inactiva' || message === 'Sesión inactiva') {
|
||||
// if (store.state.sessionInactive === false) {
|
||||
// store.actions.setSessionInactive(true)
|
||||
// }
|
||||
} else {
|
||||
showToast(i18n, toast, (message || mensaje), status === 'ERROR' ? 'error' : 'warning', i18nCode)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if ((status === 'OK' || status === 'ok' || status === 'SUCCESS' || status === 'success') && showOKMessage) {
|
||||
showToast(i18n, toast, (message || mensaje), 'success', i18nCode)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
getFileParams (model, headers, value, prefix) {
|
||||
const head = findWhere(headers, { value })
|
||||
const { filename, extension } = functions.getFilePattern(model[value])
|
||||
const params = {}
|
||||
if (head.logicalName && head.logicalName !== null) {
|
||||
if (typeof head.logicalName === 'function') {
|
||||
params.logico = head.logicalName(model)
|
||||
} else {
|
||||
params.logico = head.logicalName
|
||||
}
|
||||
} else {
|
||||
params.logico = filename
|
||||
}
|
||||
if (prefix && prefix !== null) {
|
||||
params.logico = `${prefix}${params.logico}`
|
||||
}
|
||||
if (head.logicalExtension && head.logicalExtension !== null) {
|
||||
if (typeof head.logicalExtension === 'function') {
|
||||
params.ext = head.logicalExtension(model)
|
||||
} else {
|
||||
params.ext = head.logicalExtension
|
||||
}
|
||||
} else {
|
||||
params.ext = extension
|
||||
}
|
||||
return params
|
||||
},
|
||||
validaPermisos: async (token, administra, configura, graba_modifica, consulta) => {
|
||||
let permisos = await execute('FUN_INFO_SESION_ENTIDAD', [token])
|
||||
if (permisos === undefined || permisos.length <= 0) {
|
||||
return false
|
||||
}
|
||||
if (permisos[0].status === 'ERROR') {
|
||||
return false
|
||||
} else if (permisos[0].status === 'WARNING') {
|
||||
return false
|
||||
} else {
|
||||
permisos = permisos[0]
|
||||
const secVal = []
|
||||
if (permisos.administra === 'S') {
|
||||
return true
|
||||
} else if (permisos.configura === 'S') {
|
||||
secVal.push(2)
|
||||
} else if (permisos.graba_modifica === 'S') {
|
||||
secVal.push(3)
|
||||
} else if (permisos.consulta === 'S') {
|
||||
secVal.push(4)
|
||||
}
|
||||
if (configura === 'S' && secVal.includes(2) === true) {
|
||||
return true
|
||||
} else if (graba_modifica === 'S' && secVal.includes(3) === true) {
|
||||
return true
|
||||
} else if (consulta === 'S' && secVal.includes(4) === true) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
i18nHeaders: (i18n, headers) => {
|
||||
return new Promise((resolve) => {
|
||||
const newHeaders = []
|
||||
for (const hds of headers) {
|
||||
if (hds.i18n && hds.i18n.length > 0) {
|
||||
hds.text = i18n.t(hds.i18n)
|
||||
}
|
||||
if (hds.pristineMessages) {
|
||||
Object.keys(hds.pristineMessages).forEach((msg) => {
|
||||
if (msg.includes('-params') === false) {
|
||||
if (hds.pristineMessages[`${msg}-params`]) {
|
||||
hds.inputProps[`data-pristine-${msg}-message`] = hds.pristineMessages[msg]
|
||||
? i18n.t(hds.pristineMessages[msg], hds.pristineMessages[`${msg}-params`])
|
||||
: 'txtNone'
|
||||
} else {
|
||||
hds.inputProps[`data-pristine-${msg}-message`] = hds.pristineMessages[msg]
|
||||
? i18n.t(hds.pristineMessages[msg])
|
||||
: 'txtNone'
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
newHeaders.push(hds)
|
||||
}
|
||||
resolve(newHeaders)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default environment
|
82
src/utils/globalPresets.js
Normal file
82
src/utils/globalPresets.js
Normal file
@ -0,0 +1,82 @@
|
||||
const theme = 'blue'
|
||||
|
||||
const presets = {
|
||||
appTitle: 'Orbis Playground',
|
||||
theme: `${theme}`,
|
||||
svgIconUrl: 'https://www.via-asesores.com/svgicons/smartoperation/',
|
||||
images: {
|
||||
loginFondo: `${process.env.publicPath}/images/backgrounds.webp`,
|
||||
welcomeFondo: 'https://www.via-asesores.com/backgrounds/smartoperation/SmartOperation_background.png',
|
||||
icon: 'https://www.via-asesores.com/logos/logo_icons/smartdeveloper_icon.svg',
|
||||
logo: 'https://www.via-asesores.com/logos/logo_vertical/smartdeveloper_vertical_logo.svg',
|
||||
imageLoader: 'https://www.via-asesores.com/logos/logo_via.png',
|
||||
noImageFound: 'https://www.via-asesores.com/smartsalesnprofit/images/LogoViasaClaroTransparente_600x.png',
|
||||
onError: 'https://www.via-asesores.com/smartsalesnprofit/images/LogoViasaClaroTransparente_600x.png',
|
||||
noDataFound: `${process.env.publicPath}/images/cancel.png`
|
||||
},
|
||||
locations: {
|
||||
login: '/',
|
||||
welcome: '/dashboard',
|
||||
welcomeTemp: '/welcome'
|
||||
},
|
||||
toaster: {
|
||||
position: 'top-center',
|
||||
autoClose: 5000,
|
||||
hideProgressBar: false,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
draggable: true
|
||||
},
|
||||
pristine: {
|
||||
// class of the parent element where the error/success class is added
|
||||
classTo: 'form-group',
|
||||
errorClass: 'ring-red-400',
|
||||
successClass: 'has-success',
|
||||
// class of the parent element where error text element is appended
|
||||
errorTextParent: 'form-group',
|
||||
// type of element to create for the error text
|
||||
errorTextTag: 'div',
|
||||
// class of the error text element
|
||||
errorTextClass: 'text-help text-red-500 text-sm py-1'
|
||||
},
|
||||
graphOptions: {
|
||||
interaction: {
|
||||
dragNodes: false,
|
||||
dragView: true,
|
||||
hover: true
|
||||
},
|
||||
layout: {
|
||||
hierarchical: {
|
||||
enabled: true,
|
||||
levelSeparation: 200,
|
||||
nodeSpacing: 200,
|
||||
treeSpacing: 200,
|
||||
blockShifting: true,
|
||||
edgeMinimization: true,
|
||||
parentCentralization: true,
|
||||
direction: 'LR', // UD, DU, LR, RL
|
||||
sortMethod: 'hubsize', // hubsize, directed
|
||||
shakeTowards: 'leaves' // roots, leaves
|
||||
}
|
||||
},
|
||||
edges: {
|
||||
color: '#000000'
|
||||
},
|
||||
height: '700px',
|
||||
physics: {
|
||||
enabled: false,
|
||||
// maxVelocity: 50,
|
||||
// minVelocity: 0.1,
|
||||
// solver: "barnesHut",
|
||||
stabilization: {
|
||||
enabled: true,
|
||||
iterations: 1000,
|
||||
updateInterval: 100,
|
||||
onlyDynamicEdges: false,
|
||||
fit: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default presets
|
@ -1,20 +1,98 @@
|
||||
import type { Config } from 'tailwindcss'
|
||||
|
||||
const defaultTheme = require('tailwindcss/defaultTheme')
|
||||
|
||||
const config: Config = {
|
||||
mode: 'jit',
|
||||
content: [
|
||||
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/app/**/*.{js,ts,jsx,tsx,mdx}'
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
backgroundImage: {
|
||||
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
|
||||
'gradient-conic':
|
||||
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
|
||||
fontFamily: {
|
||||
sans: ['Inter', ...defaultTheme.fontFamily.sans],
|
||||
inter: ['Inter', 'sans-serif', ...defaultTheme.fontFamily.sans]
|
||||
},
|
||||
fontSize: {
|
||||
xs: ['0.75rem', { lineHeight: '1.25' }],
|
||||
sm: ['0.875rem', { lineHeight: '1.25' }],
|
||||
base: ['1rem', { lineHeight: '1.25', letterSpacing: '-0.01em' }],
|
||||
lg: ['1.125rem', { lineHeight: '1.25', letterSpacing: '-0.01em' }],
|
||||
xl: ['1.25rem', { lineHeight: '1.25', letterSpacing: '-0.01em' }],
|
||||
'2xl': ['1.5rem', { lineHeight: '1.33', letterSpacing: '-0.01em' }],
|
||||
'3xl': ['1.88rem', { lineHeight: '1.33', letterSpacing: '-0.01em' }],
|
||||
'4xl': ['2.25rem', { lineHeight: '1.25', letterSpacing: '-0.02em' }],
|
||||
'5xl': ['3rem', { lineHeight: '1.25', letterSpacing: '-0.02em' }],
|
||||
'6xl': ['3.75rem', { lineHeight: '1.2', letterSpacing: '-0.02em' }]
|
||||
},
|
||||
screens: {
|
||||
xs: '480px'
|
||||
},
|
||||
plugins: [],
|
||||
borderWidth: {
|
||||
3: '3px'
|
||||
},
|
||||
minWidth: {
|
||||
36: '9rem',
|
||||
44: '11rem',
|
||||
56: '14rem',
|
||||
60: '15rem',
|
||||
72: '18rem',
|
||||
80: '20rem'
|
||||
},
|
||||
maxWidth: {
|
||||
'8xl': '88rem',
|
||||
'9xl': '96rem'
|
||||
},
|
||||
zIndex: {
|
||||
60: '60'
|
||||
},
|
||||
boxShadow: {
|
||||
DEFAULT: '0 1px 3px 0 rgba(0, 0, 0, 0.08), 0 1px 2px 0 rgba(0, 0, 0, 0.02)',
|
||||
md: '0 4px 6px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -1px rgba(0, 0, 0, 0.02)',
|
||||
lg: '0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -2px rgba(0, 0, 0, 0.01)',
|
||||
xl: '0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 10px 10px -5px rgba(0, 0, 0, 0.01)'
|
||||
// neon: '0 0 3px #7f11e0, 0 0 6px #7f11e0, 0 0 9px #7f11e0',
|
||||
// 'neon-hover': '0 0 6px #7f11e0, 0 0 12px #7f11e0, 0 0 18px #7f11e0'
|
||||
},
|
||||
outline: {
|
||||
blue: '2px solid rgba(0, 112, 244, 0.5)'
|
||||
},
|
||||
colors: {
|
||||
'theme-text': {
|
||||
principal: '#323338',
|
||||
disabled: '#cbd5e1'
|
||||
},
|
||||
'theme-app': {
|
||||
50: '#e1ddeb',
|
||||
100: '#bab0d6',
|
||||
200: '#9383c1',
|
||||
300: '#6c56ac',
|
||||
400: '#462997',
|
||||
500: '#361e7b',
|
||||
600: '#311b70',
|
||||
700: '#2c1864',
|
||||
800: '#261558',
|
||||
900: '#21114d',
|
||||
950: '#1b0e41'
|
||||
},
|
||||
warning: '#3B82F6',
|
||||
'warning-dark': '#1E40AF',
|
||||
'warning-hover': '#60A5FA',
|
||||
'warning-hover-dark': '#2563EB',
|
||||
danger: '#EF4444',
|
||||
'danger-dark': '#991B1B',
|
||||
'danger-hover': '#F87171',
|
||||
'danger-hover-dark': '#DC2626',
|
||||
success: '#22C55E',
|
||||
'success-dark': '#15803D',
|
||||
'success-hover': '#34D399',
|
||||
'success-hover-dark': '#22C55E'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: []
|
||||
}
|
||||
export default config
|
||||
|
@ -1,22 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
Loading…
Reference in New Issue
Block a user