diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cf7c91e..5ca60f3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,6 +10,8 @@ "dependencies": { "@mantine/core": "8.0.0", "@mantine/hooks": "8.0.0", + "@react-three/fiber": "^9.1.2", + "@tabler/icons-react": "^3.31.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-router-dom": "^7.4.0" @@ -1474,6 +1476,62 @@ "node": ">=14" } }, + "node_modules/@react-three/fiber": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.1.2.tgz", + "integrity": "sha512-k8FR9yVHV9kIF3iuOD0ds5hVymXYXfgdKklqziBVod9ZEJ8uk05Zjw29J/omU3IKeUfLNAIHfxneN3TUYM4I2w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@types/react-reconciler": "^0.28.9", + "@types/webxr": "*", + "base64-js": "^1.5.1", + "buffer": "^6.0.3", + "its-fine": "^2.0.0", + "react-reconciler": "^0.31.0", + "react-use-measure": "^2.1.7", + "scheduler": "^0.25.0", + "suspend-react": "^0.1.3", + "use-sync-external-store": "^1.4.0", + "zustand": "^5.0.3" + }, + "peerDependencies": { + "expo": ">=43.0", + "expo-asset": ">=8.4", + "expo-file-system": ">=11.0", + "expo-gl": ">=11.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-native": ">=0.78", + "three": ">=0.156" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + }, + "expo-asset": { + "optional": true + }, + "expo-file-system": { + "optional": true + }, + "expo-gl": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@react-three/fiber/node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" + }, "node_modules/@rollup/pluginutils": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", @@ -2027,6 +2085,32 @@ "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, + "node_modules/@tabler/icons": { + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.31.0.tgz", + "integrity": "sha512-dblAdeKY3+GA1U+Q9eziZ0ooVlZMHsE8dqP0RkwvRtEsAULoKOYaCUOcJ4oW1DjWegdxk++UAt2SlQVnmeHv+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + } + }, + "node_modules/@tabler/icons-react": { + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.31.0.tgz", + "integrity": "sha512-2rrCM5y/VnaVKnORpDdAua9SEGuJKVqPtWxeQ/vUVsgaUx30LDgBZph7/lterXxDY1IKR6NO//HDhWiifXTi3w==", + "license": "MIT", + "dependencies": { + "@tabler/icons": "3.31.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + }, + "peerDependencies": { + "react": ">= 16" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", @@ -2218,7 +2302,6 @@ "version": "19.1.2", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", - "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -2234,6 +2317,15 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/react-reconciler": { + "version": "0.28.9", + "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", + "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.20.6", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz", @@ -2241,6 +2333,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/webxr": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.22.tgz", + "integrity": "sha512-Vr6Stjv5jPRqH690f5I5GLjVk8GSsoQSYJ2FVd/3jJF7KaqfwPi3ehfBS96mlQ2kPCwZaX6U0rG2+NGHBKkA/A==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.32.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.0.tgz", @@ -2940,7 +3038,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -3058,7 +3155,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, "funding": [ { "type": "github", @@ -3466,7 +3562,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -5199,7 +5294,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -5799,6 +5893,18 @@ "node": ">= 0.4" } }, + "node_modules/its-fine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz", + "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==", + "license": "MIT", + "dependencies": { + "@types/react-reconciler": "^0.28.9" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -7239,6 +7345,27 @@ "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-reconciler": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.31.0.tgz", + "integrity": "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.25.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/react-reconciler/node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -7383,6 +7510,21 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-use-measure": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", + "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.13", + "react-dom": ">=16.13" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, "node_modules/recast": { "version": "0.23.11", "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", @@ -8695,6 +8837,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/suspend-react": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", + "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=17.0" + } + }, "node_modules/svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", @@ -8790,6 +8941,13 @@ "node": ">=8" } }, + "node_modules/three": { + "version": "0.176.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.176.0.tgz", + "integrity": "sha512-PWRKYWQo23ojf9oZSlRGH8K09q7nRSWx6LY/HF/UUrMdYgN9i1e2OwJYHoQjwc6HF/4lvvYLC5YC1X8UJL2ZpA==", + "license": "MIT", + "peer": true + }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -9332,6 +9490,15 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -9935,6 +10102,35 @@ "peerDependencies": { "zod": "^3.24.1" } + }, + "node_modules/zustand": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.4.tgz", + "integrity": "sha512-39VFTN5InDtMd28ZhjLyuTnlytDr9HfwO512Ai4I8ZABCoyAj4F1+sr7sD1jP/+p7k77Iko0Pb5NhgBFDCX0kQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/frontend/package.json b/frontend/package.json index 17dfe17..7cc9f93 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,8 @@ "dependencies": { "@mantine/core": "8.0.0", "@mantine/hooks": "8.0.0", + "@react-three/fiber": "^9.1.2", + "@tabler/icons-react": "^3.31.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-router-dom": "^7.4.0" diff --git a/frontend/src/Router.tsx b/frontend/src/Router.tsx index b8c7bf2..e426086 100644 --- a/frontend/src/Router.tsx +++ b/frontend/src/Router.tsx @@ -1,11 +1,21 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import { HomePage } from './pages/Home.page'; +import { Consulta_API } from './pages/Consulta_api'; +import { Error_404 } from './pages/404'; // Ajusta si está en otra carpeta const router = createBrowserRouter([ { path: '/', element: , }, + { + path: '/Consulta_API', + element: , + }, + { + path: '*', + element: , + }, ]); export function Router() { diff --git a/frontend/src/components/DoubleNavbar.module.css b/frontend/src/components/DoubleNavbar.module.css new file mode 100644 index 0000000..195348f --- /dev/null +++ b/frontend/src/components/DoubleNavbar.module.css @@ -0,0 +1,114 @@ +.navbar { + height: 100vh; /* ← Ocupa todo el alto de la ventana */ + display: flex; + flex-direction: column; + background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-6)); + width: 300px; + border-right: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + position: sticky; /* ← Opcional, si quieres que se quede "pegado" */ + top: 0; /* ← Ancla arriba */ +} + + + + + .collapsed { + width: 70px; /* Ancho reducido cuando colapsa */ + } + + .wrapper { + display: flex; + flex: 1; + } + + .collapseButton { + margin-top: auto; /* Hace que el botón se empuje hacia abajo */ + margin-bottom: 16px; /* ← Agrega separación del borde inferior */ + + } + + .aside { + flex: 0 0 60px; + background-color: var(--mantine-color-body); + display: flex; + flex-direction: column; + align-items: center; + border-right: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-7)); + } + + .main { + flex: 1; + background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); + } + + .mainLink { + width: 44px; + height: 44px; + border-radius: var(--mantine-radius-md); + display: flex; + align-items: center; + justify-content: center; + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0)); + + &:hover { + background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-5)); + } + + &[data-active] { + &, + &:hover { + background-color: var(--mantine-color-blue-light); + color: var(--mantine-color-blue-light-color); + } + } + } + + .title { + font-family: + Greycliff CF, + var(--mantine-font-family); + margin-bottom: var(--mantine-spacing-xl); + background-color: var(--mantine-color-body); + padding: var(--mantine-spacing-md); + padding-top: 18px; + height: 60px; + border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-7)); + } + + .logo { + width: 100%; + display: flex; + justify-content: center; + height: 60px; + padding-top: var(--mantine-spacing-md); + border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-7)); + margin-bottom: var(--mantine-spacing-xl); + } + + .link { + display: block; + text-decoration: none; + border-top-right-radius: var(--mantine-radius-md); + border-bottom-right-radius: var(--mantine-radius-md); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0)); + padding: 0 var(--mantine-spacing-md); + font-size: var(--mantine-font-size-sm); + margin-right: var(--mantine-spacing-md); + font-weight: 500; + height: 44px; + line-height: 44px; + + &:hover { + background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5)); + color: light-dark(var(--mantine-color-dark), var(--mantine-color-light)); + } + + &[data-active] { + &, + &:hover { + border-left-color: var(--mantine-color-blue-filled); + background-color: var(--mantine-color-blue-filled); + color: var(--mantine-color-white); + } + } + } \ No newline at end of file diff --git a/frontend/src/components/DoubleNavbar.tsx b/frontend/src/components/DoubleNavbar.tsx new file mode 100644 index 0000000..5c08ea1 --- /dev/null +++ b/frontend/src/components/DoubleNavbar.tsx @@ -0,0 +1,155 @@ +import { + IconCalendarStats, + IconDeviceDesktopAnalytics, + IconFingerprint, + IconGauge, + IconHome2, + IconSettings, + IconUser, + IconArrowBarLeft, + IconArrowBarRight, +} from '@tabler/icons-react'; +import { + Title, + Tooltip, + UnstyledButton, + ActionIcon, +} from '@mantine/core'; +import { Link, useLocation } from 'react-router-dom'; +import { useEffect, useState } from 'react'; +import classes from './DoubleNavbar.module.css'; + +const mainLinksdata = [ + { icon: IconHome2, label: 'Home' }, + { icon: IconGauge, label: 'Dashboard' }, + { icon: IconDeviceDesktopAnalytics, label: 'Analytics' }, + { icon: IconCalendarStats, label: 'Releases' }, + { icon: IconUser, label: 'Account' }, + { icon: IconFingerprint, label: 'Security' }, + { icon: IconSettings, label: 'Settings' }, +]; + +const submenuLinks: Record = { + Home: [ + { label: 'Inicio', to: '/' }, + { label: 'Consulta Api', to: '/Consulta_API' }, + ], + Dashboard: [ + { label: 'Resumen', to: '/dashboard/resumen' }, + { label: 'Estadísticas', to: '/dashboard/estadisticas' }, + { label: 'Usuarios', to: '/dashboard/usuarios' }, + ], + Analytics: [ + { label: 'Conversiones', to: '/analytics/conversiones' }, + { label: 'Tráfico', to: '/analytics/trafico' }, + { label: 'Tendencias', to: '/analytics/tendencias' }, + ], + Releases: [ + { label: 'Notas de versión', to: '/releases/notas-de-version' }, + { label: 'Historial', to: '/releases/historial' }, + ], + Account: [ + { label: 'Perfil', to: '/account/perfil' }, + { label: 'Suscripciones', to: '/account/suscripciones' }, + ], + Security: [ + { label: 'Contraseña', to: '/security/contraseña' }, + { label: '2FA', to: '/security/2fa' }, + ], + Settings: [ + { label: 'Preferencias', to: '/settings/preferencias' }, + { label: 'Notificaciones', to: '/settings/notificaciones' }, + ], +}; + +export function DoubleNavbar() { + const location = useLocation(); + const [collapsed, setCollapsed] = useState(false); + const [manualActiveTab, setManualActiveTab] = useState(null); + + // Detectar cuál pestaña es activa por la ruta actual + const matchedMain = Object.entries(submenuLinks).find(([mainKey, items]) => + items.some((item) => location.pathname.startsWith(item.to)) + ); + + const routeBasedActive = matchedMain?.[0] ?? 'Home'; + const active = manualActiveTab ?? routeBasedActive; + + const activeLink = + submenuLinks[active]?.find((item) => location.pathname === item.to)?.label ?? ''; + + const mainLinks = mainLinksdata.map((link) => ( + + setManualActiveTab(link.label)} + className={classes.mainLink} + data-active={link.label === active || undefined} + > + + + + )); + + const links = (submenuLinks[active] || []).map((item) => ( + + {item.label} + + )); + + // Resetea pestaña seleccionada si la ruta cambia (opcional) + useEffect(() => { + setManualActiveTab(null); + }, [location.pathname]); + + return ( + + ); +} \ No newline at end of file diff --git a/frontend/src/components/HoloShader.tsx b/frontend/src/components/HoloShader.tsx new file mode 100644 index 0000000..c002da8 --- /dev/null +++ b/frontend/src/components/HoloShader.tsx @@ -0,0 +1,140 @@ +import { Card, Title, Box, useMantineTheme } from '@mantine/core'; +import { Canvas, extend, useFrame, useThree } from '@react-three/fiber'; +import { useRef, useMemo } from 'react'; +import * as THREE from 'three'; + +// 🎨 Utilidad para convertir hex a RGB [0–1] +function hexToRGBArray(hex: string): [number, number, number] { + const bigint = parseInt(hex.replace('#', ''), 16); + return [ + ((bigint >> 16) & 255) / 255, + ((bigint >> 8) & 255) / 255, + (bigint & 255) / 255, + ]; +} + +// ✨ Shader personalizado estilo holográfico, con color dinámico +class HoloShaderMaterial extends THREE.ShaderMaterial { + constructor(color: [number, number, number]) { + super({ + uniforms: { + u_time: { value: 0 }, + u_resolution: { value: new THREE.Vector2() }, + u_color: { value: new THREE.Vector3(...color) }, + }, + vertexShader: ` + void main() { + gl_Position = vec4(position, 1.0); + } + `, + fragmentShader: ` + precision mediump float; + uniform float u_time; + uniform vec2 u_resolution; + uniform vec3 u_color; + + void main() { + vec2 uv = gl_FragCoord.xy / u_resolution; + vec2 pos = uv * 10.0; + pos.x += u_time * 0.3; + pos.y += sin(u_time * 0.2) * 2.0; + + float color = sin(pos.x + sin(pos.y + sin(pos.x))) * 0.5 + 0.5; + + vec3 c = vec3( + u_color.r + 0.2 * sin(u_time + pos.x), + u_color.g + 0.2 * cos(u_time + pos.y), + u_color.b + 0.2 * sin(pos.x + pos.y + u_time) + ); + + gl_FragColor = vec4(c * color, 1.0); + } + `, + }); + } +} + +extend({ HoloShaderMaterial }); + +// 🎥 Plano con el shader +function HoloPlane({ color }: { color: [number, number, number] }) { + const mat = useRef(); + const { size } = useThree(); + + useFrame(({ clock }) => { + if (mat.current) { + mat.current.uniforms.u_time.value = clock.getElapsedTime(); + mat.current.uniforms.u_resolution.value.set(size.width, size.height); + } + }); + + const material = useMemo(() => new HoloShaderMaterial(color), [color]); + + return ( + + + + + ); +} + +// 🎨 Fondo que ocupa todo el contenedor +function HolographicBackground({ color }: { color: [number, number, number] }) { + return ( + + + + + + ); +} + +// 🧩 Componente final con fondo shader y texto 404 +export function MantineCardWithShader() { + const theme = useMantineTheme(); + const hex = theme.colors[theme.primaryColor][6]; + const rgb = hexToRGBArray(hex); + + return ( + + + + + + 404 + + + + ); +} diff --git a/frontend/src/components/MetodoSelect.tsx b/frontend/src/components/MetodoSelect.tsx new file mode 100644 index 0000000..81fad48 --- /dev/null +++ b/frontend/src/components/MetodoSelect.tsx @@ -0,0 +1,52 @@ +import { Select, Group } from '@mantine/core'; +import { IconCheck } from '@tabler/icons-react'; + +interface MetodoSelectProps { + metodo: string; + setMetodo: (value: string) => void; +} + +const metodoData = [ + { value: 'GET', label: 'GET' }, + { value: 'POST', label: 'POST' }, + { value: 'PUT', label: 'PUT' }, + { value: 'DELETE', label: 'DELETE' }, +]; + +const colorMap: Record = { + GET: '#10b981', // verde + POST: '#3b82f6', // azul + PUT: '#f59e0b', // naranja + DELETE: '#ef4444', // rojo +}; + +const backgroundMap: Record = { + GET: '#d1fae5', + POST: '#e0f2fe', + PUT: '#fef3c7', + DELETE: '#fee2e2', +}; + +export function MetodoSelect({ metodo, setMetodo }: MetodoSelectProps) { + return ( +