feat(CodePlayground): ✨ Visualization of component code in real time
This commit is contained in:
parent
36757bd163
commit
10e935d2e7
36
.vscode/settings.json
vendored
36
.vscode/settings.json
vendored
@ -11,25 +11,25 @@
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#0894ba",
|
||||
"activityBar.background": "#0894ba",
|
||||
"activityBar.foreground": "#e7e7e7",
|
||||
"activityBar.inactiveForeground": "#e7e7e799",
|
||||
"activityBarBadge.background": "#99077a",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"commandCenter.border": "#e7e7e799",
|
||||
"sash.hoverBorder": "#0894ba",
|
||||
"statusBar.background": "#066d89",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#0894ba",
|
||||
"statusBarItem.remoteBackground": "#066d89",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#066d89",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#066d8999",
|
||||
"titleBar.inactiveForeground": "#e7e7e799"
|
||||
"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": "#066d89",
|
||||
"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"]
|
@ -1,12 +1,12 @@
|
||||
{
|
||||
"constants": {
|
||||
"plataforma": "web",
|
||||
"publicPath": "/orbistemplate",
|
||||
"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": "OrbisTemplate",
|
||||
"idApp": "orbistemplate",
|
||||
"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"
|
||||
|
@ -1,12 +1,12 @@
|
||||
{
|
||||
"constants": {
|
||||
"plataforma": "web",
|
||||
"publicPath": "/orbistemplate",
|
||||
"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": "OrbisTemplate",
|
||||
"idApp": "orbistemplate",
|
||||
"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"
|
||||
|
@ -1,12 +1,12 @@
|
||||
{
|
||||
"constants": {
|
||||
"plataforma": "web",
|
||||
"publicPath": "/orbistemplate",
|
||||
"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": "OrbisTemplate",
|
||||
"idApp": "orbistemplate",
|
||||
"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"
|
||||
|
@ -1,12 +1,12 @@
|
||||
{
|
||||
"constants": {
|
||||
"plataforma": "web",
|
||||
"publicPath": "/orbistemplate",
|
||||
"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": "OrbisTemplate",
|
||||
"idApp": "orbistemplate",
|
||||
"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/*"]
|
||||
}
|
||||
}
|
||||
}
|
17
package.json
17
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "next-template",
|
||||
"name": "orbis-playground",
|
||||
"version": "0.1.0",
|
||||
"description": "My Next.js project",
|
||||
"description": "Orbis Playground",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.via-asesores.com/jmaritar/next-template"
|
||||
@ -25,7 +25,13 @@
|
||||
"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",
|
||||
@ -36,10 +42,15 @@
|
||||
"react-dom": "^18",
|
||||
"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",
|
||||
"vComponents": "git+https://2bdcc0300e0ed5ac01f9dcd51368f7ac74fdb85a@git.via-asesores.com/libraries/v-components.git"
|
||||
"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": {
|
||||
"@types/node": "^20",
|
||||
|
@ -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 |
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
|
@ -90,7 +90,7 @@ const FormLogin = ({ onLogin }) => {
|
||||
onChange={(e) => { validate(e) }}
|
||||
/>
|
||||
<button
|
||||
className={'via-append-input'}
|
||||
className={'via-append-input shadow-neon'}
|
||||
onClick={(e) => { e.stopPropagation() }}
|
||||
disabled
|
||||
>
|
||||
@ -115,7 +115,7 @@ const FormLogin = ({ onLogin }) => {
|
||||
onChange={(e) => { validate(e) }}
|
||||
/>
|
||||
<button
|
||||
className={'via-append-input'}
|
||||
className={'via-append-input shadow-neon'}
|
||||
onClick={(e) => { e.stopPropagation(); e.preventDefault(); setViewPassword(!viewPassword) }}
|
||||
>
|
||||
{ viewPassword === false && <EyeIcon className="h-5 w-5" /> }
|
||||
@ -127,7 +127,11 @@ const FormLogin = ({ onLogin }) => {
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!isInputValid}
|
||||
className={`${'via-button'} ${(isInputValid ? 'bg-cyan-900' : 'bg-red-400')} mx-0 w-full justify-center`}
|
||||
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') }
|
||||
|
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>
|
||||
)
|
||||
}
|
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>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
@ -6,17 +6,17 @@ import { TbColorPicker, TbLanguage } from 'react-icons/tb'
|
||||
const UserOptionsMenu = ({ setPreferences, theme, i18n }) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-center text-gray-900">
|
||||
<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-4 grid grid-cols-2 gap-2 p-1">
|
||||
<div className="mb-2 grid grid-cols-2 gap-2 p-1">
|
||||
<Menu.Item>
|
||||
<button
|
||||
className={`${
|
||||
theme === 'light'
|
||||
? 'bg-slate-700 text-white dark:bg-slate-800'
|
||||
: 'bg-gray-200 text-gray-900 transition-colors duration-[350ms] ease-in-out hover:bg-gray-300'
|
||||
? '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')
|
||||
@ -30,8 +30,8 @@ const UserOptionsMenu = ({ setPreferences, theme, i18n }) => {
|
||||
<button
|
||||
className={`${
|
||||
theme === 'dark'
|
||||
? 'bg-slate-700 text-white dark:bg-slate-800'
|
||||
: 'bg-gray-200 text-gray-900 transition-colors duration-[350ms] ease-in-out hover:bg-gray-300'
|
||||
? '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')
|
||||
@ -43,18 +43,18 @@ const UserOptionsMenu = ({ setPreferences, theme, i18n }) => {
|
||||
</Menu.Item>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center text-gray-900">
|
||||
<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="grid grid-cols-2 gap-2 p-1">
|
||||
<div className="mb-2 grid grid-cols-2 gap-2 p-1">
|
||||
<Menu.Item>
|
||||
<button
|
||||
className={`${
|
||||
i18n.activeLocale === 'es'
|
||||
? 'bg-slate-700 text-white dark:bg-slate-800'
|
||||
: 'bg-gray-200 text-gray-900 transition-colors duration-[350ms] ease-in-out hover:bg-gray-300'
|
||||
? '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')
|
||||
@ -82,8 +82,8 @@ const UserOptionsMenu = ({ setPreferences, theme, i18n }) => {
|
||||
<button
|
||||
className={`${
|
||||
i18n.activeLocale === 'en'
|
||||
? 'bg-slate-700 text-white dark:bg-slate-800'
|
||||
: 'bg-gray-200 text-gray-900 transition-colors duration-[350ms] ease-in-out hover:bg-gray-300'
|
||||
? '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')
|
||||
|
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
|
||||
}
|
||||
]
|
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
|
@ -2,12 +2,14 @@ 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;
|
||||
}
|
||||
@ -15,6 +17,7 @@ interface IActions {
|
||||
export const { useStore, store } = createStore<IState, IActions>(
|
||||
{
|
||||
title: '',
|
||||
theme: 'light',
|
||||
usuarioPermisos: null,
|
||||
valueEasyMDE: ''
|
||||
},
|
||||
@ -24,6 +27,11 @@ export const { useStore, store } = createStore<IState, IActions>(
|
||||
title: newState
|
||||
})
|
||||
},
|
||||
setTheme: (store, newState) => {
|
||||
store.setState({
|
||||
theme: newState
|
||||
})
|
||||
},
|
||||
setUsuarioPermisos: (store, newState) => {
|
||||
store.setState({
|
||||
usuarioPermisos: newState
|
||||
|
@ -12,10 +12,12 @@ 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('vComponents/Sidebar') }, { ssr: false })
|
||||
const Sidebar = dynamic(() => { return import('@/components/vComponents/Sidebar') }, { ssr: false })
|
||||
const Navbar = dynamic(() => { return import('vComponents/Navbar') }, { ssr: false })
|
||||
|
||||
const ResponsiveContainer = ({ children }) => {
|
||||
@ -26,13 +28,14 @@ const ResponsiveContainer = ({ children }) => {
|
||||
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true)
|
||||
const [menu, setMenu] = useState([])
|
||||
const [title, setTitle] = useStore(s => s.title, a => a.setTitle)
|
||||
const [userObj, setUserObj] = useState()
|
||||
const [token, setToken] = useState('')
|
||||
const [theme, setTheme] = useState('light')
|
||||
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')
|
||||
@ -107,7 +110,8 @@ const ResponsiveContainer = ({ children }) => {
|
||||
loading.start()
|
||||
setMenu([])
|
||||
if (token && token !== null) {
|
||||
const options = await execute('SPR_MENU_S', [token, 'BO', null])
|
||||
// const options = await execute('SPR_MENU_S', [token, 'BO', null])
|
||||
const options = menuData
|
||||
setMenu(options)
|
||||
}
|
||||
loading.stop()
|
||||
@ -232,6 +236,7 @@ const ResponsiveContainer = ({ children }) => {
|
||||
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} />}
|
||||
@ -243,7 +248,7 @@ const ResponsiveContainer = ({ children }) => {
|
||||
}
|
||||
|
||||
<main>
|
||||
<div className={userObj && userObj.nombre_usuario ? 'responsive-container' : 'hidden'}>
|
||||
<div className={userObj && userObj.nombre_usuario ? 'responsive-container' : ''}>
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
@ -252,7 +257,7 @@ const ResponsiveContainer = ({ children }) => {
|
||||
<SessionTimeout i18n={i18n} user={userObj} onTimeout={() => onTimeout()} onCancelTimeout={() => onCancelTimeout()} />
|
||||
}
|
||||
</div>
|
||||
<Footer version={appVersion} label={i18n.t('common.version') || 'version'} />
|
||||
<Footer footerStyle='via-footer' version={appVersion} label={i18n.t('common.version') || 'version'} />
|
||||
</div>
|
||||
</LayoutContext.Provider>
|
||||
)
|
||||
|
@ -1,27 +1,34 @@
|
||||
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 '@/styles/globals.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
import Head from 'next/head'
|
||||
import 'vComponents/styles/generated/output.css'
|
||||
import 'vComponents/styles/generated/bgColors.min.css'
|
||||
|
||||
import { ToastContainer } from 'react-toastify'
|
||||
|
||||
export default function App ({ Component, pageProps }: AppProps) {
|
||||
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 Template</title>
|
||||
<title>Orbis Playground</title>
|
||||
</Head>
|
||||
|
||||
<I18nProvider locale={'es'} dict={''}>
|
||||
<LoadingProvider>
|
||||
<ToastContainer />
|
||||
<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>
|
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
|
@ -56,12 +56,12 @@ const LoginPage = () => {
|
||||
max_mb_upload: process.env.maxMbUpload
|
||||
}
|
||||
await environment.login(user.token, user, constante)
|
||||
toast.success(`Bienvenido ${user.nombre_usuario}`, presets.toaster)
|
||||
await global.setTheme(user.theme)
|
||||
await global.setUserObj(user)
|
||||
await global.setConstanteObj(constante)
|
||||
await global.setToken(resultado[0].token)
|
||||
await global.setTheme(user.theme)
|
||||
await i18n.locale(user.i18n)
|
||||
toast.success(`Bienvenido ${user.nombre_usuario}`, presets.toaster)
|
||||
router.push(homePath)
|
||||
}
|
||||
} catch (error) {
|
||||
|
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
|
@ -1,9 +1,9 @@
|
||||
.via-navbar {
|
||||
@apply bg-white dark:bg-[#222222] border-slate-200 text-theme-app-500 dark:text-theme-app-50 font-semibold !important;
|
||||
@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 !important;
|
||||
@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 {
|
||||
@ -11,9 +11,17 @@
|
||||
}
|
||||
|
||||
.via-user-options-navbar {
|
||||
@apply text-gray-900 bg-transparent transition-colors duration-[350ms] ease-in-out !important;
|
||||
@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-slate-700 dark:bg-slate-800 text-white transition-colors duration-[350ms] ease-in-out !important;
|
||||
@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;
|
||||
}
|
@ -3,11 +3,11 @@
|
||||
}
|
||||
|
||||
.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-700 first-letter:uppercase !important;
|
||||
@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-800 !important;
|
||||
@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 {
|
||||
@ -29,3 +29,12 @@
|
||||
.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;
|
||||
}
|
@ -26,6 +26,58 @@
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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 */
|
||||
|
@ -110,7 +110,7 @@
|
||||
}
|
||||
|
||||
.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;
|
||||
@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 {
|
||||
|
@ -11,7 +11,7 @@
|
||||
}
|
||||
|
||||
.via-login-logo {
|
||||
@apply w-64 h-64 bg-center bg-auto bg-no-repeat object-cover;
|
||||
@apply w-64 h-64 bg-center bg-auto bg-no-repeat object-contain !important;
|
||||
}
|
||||
|
||||
.via-login-background {
|
||||
|
@ -1,14 +1,14 @@
|
||||
const theme = 'blue'
|
||||
|
||||
const presets = {
|
||||
appTitle: 'Orbis Template',
|
||||
appTitle: 'Orbis Playground',
|
||||
theme: `${theme}`,
|
||||
svgIconUrl: 'https://www.via-asesores.com/svgicons/smartoperation/',
|
||||
images: {
|
||||
loginFondo: 'https://www.via-asesores.com/backgrounds/smartoperation/SmartOperation_background.png',
|
||||
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/smarterp_icon.svg',
|
||||
logo: 'https://www.via-asesores.com/logos/smartOperations.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',
|
||||
@ -25,8 +25,7 @@ const presets = {
|
||||
hideProgressBar: false,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
draggable: true,
|
||||
theme: 'light'
|
||||
draggable: true
|
||||
},
|
||||
pristine: {
|
||||
// class of the parent element where the error/success class is added
|
||||
|
@ -54,6 +54,8 @@ const config: Config = {
|
||||
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)'
|
||||
@ -64,17 +66,17 @@ const config: Config = {
|
||||
disabled: '#cbd5e1'
|
||||
},
|
||||
'theme-app': {
|
||||
50: '#DDE3EB',
|
||||
100: '#B0C4D6',
|
||||
200: '#83A5C1',
|
||||
300: '#5686AC',
|
||||
400: '#296897',
|
||||
500: '#1E507B',
|
||||
600: '#1B4870',
|
||||
700: '#183F64',
|
||||
800: '#153758',
|
||||
900: '#112F4D',
|
||||
950: '#0E2741'
|
||||
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',
|
||||
|
Loading…
Reference in New Issue
Block a user