diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5ca60f3..0ca7498 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -28,6 +28,7 @@ "@types/node": "^22.13.11", "@types/react": "^19.0.12", "@types/react-dom": "^19.0.4", + "@types/three": "^0.176.0", "@vitejs/plugin-react": "^4.3.4", "eslint": "^9.23.0", "eslint-config-mantine": "^4.0.3", @@ -533,6 +534,13 @@ "@csstools/css-tokenizer": "^3.0.3" } }, + "node_modules/@dimforge/rapier3d-compat": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@dual-bundle/import-meta-resolve": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", @@ -2215,6 +2223,13 @@ "@testing-library/dom": ">=7.21.4" } }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -2333,6 +2348,29 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.176.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.176.0.tgz", + "integrity": "sha512-FwfPXxCqOtP7EdYMagCFePNKoG1AGBDUEVKtluv2BTVRpSt7b+X27xNsirPCTCqY1pGYsPUzaM3jgWP7dXSxlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dimforge/rapier3d-compat": "^0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": "*", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~0.18.1" + } + }, "node_modules/@types/webxr": { "version": "0.5.22", "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.22.tgz", @@ -2688,6 +2726,13 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@webgpu/types": { + "version": "0.1.60", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.60.tgz", + "integrity": "sha512-8B/tdfRFKdrnejqmvq95ogp8tf52oZ51p3f4QD5m5Paey/qlX4Rhhy5Y8tgFMi7Ms70HzcMMw3EQjH/jdhTwlA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -4635,6 +4680,13 @@ } } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -6307,6 +6359,13 @@ "node": ">= 8" } }, + "node_modules/meshoptimizer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", + "dev": true, + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", diff --git a/frontend/package.json b/frontend/package.json index 7cc9f93..3370b24 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -40,6 +40,7 @@ "@types/node": "^22.13.11", "@types/react": "^19.0.12", "@types/react-dom": "^19.0.4", + "@types/three": "^0.176.0", "@vitejs/plugin-react": "^4.3.4", "eslint": "^9.23.0", "eslint-config-mantine": "^4.0.3", diff --git a/frontend/src/Router.tsx b/frontend/src/Router.tsx index e426086..4d89c9b 100644 --- a/frontend/src/Router.tsx +++ b/frontend/src/Router.tsx @@ -2,6 +2,7 @@ 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 +import { Prueba_appshell } from './pages/Prueba_appshell'; // Ajusta si está en otra carpeta const router = createBrowserRouter([ { @@ -12,6 +13,10 @@ const router = createBrowserRouter([ path: '/Consulta_API', element: , }, + { + path: '/prueba_appshell', + element: , + }, { path: '*', element: , diff --git a/frontend/src/components/Appshell_collapse.tsx b/frontend/src/components/Appshell_collapse.tsx new file mode 100644 index 0000000..f617812 --- /dev/null +++ b/frontend/src/components/Appshell_collapse.tsx @@ -0,0 +1,41 @@ +import { useDisclosure } from '@mantine/hooks'; +import { AppShell, Burger, Group, Skeleton } from '@mantine/core'; + + +export function CollapseDesktop() { + const [mobileOpened, { toggle: toggleMobile }] = useDisclosure(); + const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true); + + return ( + + + + + + Logo + + + + Navbar + {Array(15) + .fill(0) + .map((_, index) => ( + + ))} + + Main + + ); +} \ No newline at end of file diff --git a/frontend/src/components/DoubleNavbar.tsx b/frontend/src/components/DoubleNavbar.tsx index 5c08ea1..84c07d6 100644 --- a/frontend/src/components/DoubleNavbar.tsx +++ b/frontend/src/components/DoubleNavbar.tsx @@ -15,8 +15,9 @@ import { UnstyledButton, ActionIcon, } from '@mantine/core'; +import { useDisclosure, useMediaQuery } from '@mantine/hooks'; import { Link, useLocation } from 'react-router-dom'; -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import classes from './DoubleNavbar.module.css'; const mainLinksdata = [ @@ -33,6 +34,7 @@ const submenuLinks: Record = { Home: [ { label: 'Inicio', to: '/' }, { label: 'Consulta Api', to: '/Consulta_API' }, + { label: 'Prueba_appshell', to: '/prueba_appshell' }, ], Dashboard: [ { label: 'Resumen', to: '/dashboard/resumen' }, @@ -64,10 +66,15 @@ const submenuLinks: Record = { export function DoubleNavbar() { const location = useLocation(); - const [collapsed, setCollapsed] = useState(false); + const isMobile = useMediaQuery('(max-width: 768px)'); + + const [mobileOpened, { toggle: toggleMobile, close: closeMobile }] = useDisclosure(false); + const [desktopOpened, { toggle: toggleDesktop, open: openDesktop }] = useDisclosure(true); + + const isCollapsed = useMemo(() => (isMobile ? !mobileOpened : !desktopOpened), [isMobile, mobileOpened, desktopOpened]); + 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)) ); @@ -87,7 +94,10 @@ export function DoubleNavbar() { key={link.label} > setManualActiveTab(link.label)} + onClick={() => { + setManualActiveTab(link.label); + if (isMobile) closeMobile(); + }} className={classes.mainLink} data-active={link.label === active || undefined} > @@ -102,22 +112,29 @@ export function DoubleNavbar() { data-active={activeLink === item.label || undefined} to={item.to} key={item.label} - style={{ display: collapsed ? 'none' : 'block' }} + style={{ display: isCollapsed ? 'none' : 'block' }} + onClick={() => { + if (isMobile) closeMobile(); + }} > {item.label} )); - // Resetea pestaña seleccionada si la ruta cambia (opcional) + // Restaurar pestaña al cambiar ruta (opcional pero útil) useEffect(() => { setManualActiveTab(null); }, [location.pathname]); + // Abrir navbar de escritorio si cambia a pantalla grande + useEffect(() => { + if (!isMobile) openDesktop(); + }, [isMobile, openDesktop]); + return ( -