feat: Refactor navigation structure by removing DoubleNavbar and integrating AppShellWithMenu; enhance styling with new Appshell.module.css
This commit is contained in:
+7
-25
@@ -21,11 +21,6 @@
|
||||
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;
|
||||
@@ -41,7 +36,12 @@
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
}
|
||||
|
||||
.topSection {
|
||||
padding-top: 12px; /* o la cantidad que desees */
|
||||
}
|
||||
|
||||
.mainLink {
|
||||
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: var(--mantine-radius-md);
|
||||
@@ -63,27 +63,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
@@ -1,41 +1,183 @@
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { AppShell, Burger, Group, Skeleton } from '@mantine/core';
|
||||
import {
|
||||
IconCalendarStats,
|
||||
IconDeviceDesktopAnalytics,
|
||||
IconFingerprint,
|
||||
IconGauge,
|
||||
IconHome2,
|
||||
IconSettings,
|
||||
IconUser,
|
||||
IconArrowBarLeft,
|
||||
IconArrowBarRight,
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
import {
|
||||
AppShell,
|
||||
Burger,
|
||||
Group,
|
||||
Skeleton,
|
||||
Tooltip,
|
||||
UnstyledButton,
|
||||
ActionIcon,
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
|
||||
import { useDisclosure, useMediaQuery } from '@mantine/hooks';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import classes from './Appshell.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 = {
|
||||
Home: [
|
||||
{ label: 'Inicio', to: '/' },
|
||||
{ label: 'Consulta Api', to: '/Consulta_API' },
|
||||
{ label: 'Prueba_appshell', to: '/prueba_appshell' },
|
||||
],
|
||||
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 AppShellWithMenu() {
|
||||
const location = useLocation();
|
||||
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<string | null>(null);
|
||||
|
||||
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 as keyof typeof submenuLinks]?.find((item) => location.pathname === item.to)?.label ?? '';
|
||||
|
||||
const mainLinks = mainLinksdata.map((link) => (
|
||||
<Tooltip
|
||||
label={link.label}
|
||||
position="right"
|
||||
withArrow
|
||||
transitionProps={{ duration: 0 }}
|
||||
key={link.label}
|
||||
>
|
||||
<UnstyledButton
|
||||
onClick={() => {
|
||||
setManualActiveTab(link.label);
|
||||
if (isMobile) closeMobile();
|
||||
}}
|
||||
className={classes.mainLink}
|
||||
data-active={link.label === active || undefined}
|
||||
>
|
||||
<link.icon size={22} stroke={1.5} />
|
||||
</UnstyledButton>
|
||||
</Tooltip>
|
||||
));
|
||||
|
||||
const links = (submenuLinks[active as keyof typeof submenuLinks] || []).map((item) => (
|
||||
<Link
|
||||
className={classes.link}
|
||||
data-active={activeLink === item.label || undefined}
|
||||
to={item.to}
|
||||
key={item.label}
|
||||
style={{ display: isCollapsed ? 'none' : 'block' }}
|
||||
onClick={() => {
|
||||
if (isMobile) closeMobile();
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
));
|
||||
|
||||
useEffect(() => {
|
||||
setManualActiveTab(null);
|
||||
}, [location.pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMobile) openDesktop();
|
||||
}, [isMobile, openDesktop]);
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
header={{ height: 60 }}
|
||||
navbar={{
|
||||
width: isCollapsed ? 60 : 300,
|
||||
breakpoint: 'sm',
|
||||
collapsed: { mobile: !mobileOpened, desktop: !desktopOpened },
|
||||
}}
|
||||
padding="md"
|
||||
>
|
||||
|
||||
{/* Header */}
|
||||
|
||||
<AppShell.Header>
|
||||
<Group h="100%" px="md">
|
||||
<Burger opened={mobileOpened} onClick={toggleMobile} hiddenFrom="sm" size="sm" />
|
||||
<Burger opened={desktopOpened} onClick={toggleDesktop} visibleFrom="sm" size="sm" />
|
||||
<img src="/src/favicon.svg" alt="Logo" style={{ width: 30, height: 30 }} />
|
||||
</Group>
|
||||
</AppShell.Header>
|
||||
|
||||
|
||||
export function CollapseDesktop() {
|
||||
const [mobileOpened, { toggle: toggleMobile }] = useDisclosure();
|
||||
const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true);
|
||||
{/* Navbar */}
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
header={{ height: 60 }}
|
||||
navbar={{
|
||||
width: 300,
|
||||
breakpoint: 'sm',
|
||||
collapsed: { mobile: !mobileOpened, desktop: !desktopOpened },
|
||||
}}
|
||||
padding="md"
|
||||
>
|
||||
<AppShell.Header>
|
||||
<Group h="100%" px="md">
|
||||
<Burger opened={mobileOpened} onClick={toggleMobile} hiddenFrom="sm" size="sm" />
|
||||
<Burger opened={desktopOpened} onClick={toggleDesktop} visibleFrom="sm" size="sm" />
|
||||
<img
|
||||
src="/src/favicon.svg"
|
||||
alt="Logo"
|
||||
style={{ width: 30, height: 30 }}
|
||||
/>
|
||||
</Group>
|
||||
</AppShell.Header>
|
||||
<AppShell.Navbar p="md">
|
||||
Navbar
|
||||
{Array(15)
|
||||
.fill(0)
|
||||
.map((_, index) => (
|
||||
<Skeleton key={index} h={28} mt="sm" animate={false} />
|
||||
))}
|
||||
</AppShell.Navbar>
|
||||
<AppShell.Main>Main</AppShell.Main>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
<AppShell.Navbar>
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.aside}>
|
||||
<div className={classes.topSection}>
|
||||
|
||||
{mainLinks}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={classes.main}>
|
||||
{!isCollapsed && <Title order={4} className={classes.title}>{active}</Title>}
|
||||
{links}
|
||||
</div>
|
||||
</div>
|
||||
</AppShell.Navbar>
|
||||
|
||||
{/* Main Content */}
|
||||
|
||||
<AppShell.Main>
|
||||
{/* Aquí va el contenido principal */}
|
||||
Main Content
|
||||
</AppShell.Main>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
import {
|
||||
IconCalendarStats,
|
||||
IconDeviceDesktopAnalytics,
|
||||
IconFingerprint,
|
||||
IconGauge,
|
||||
IconHome2,
|
||||
IconSettings,
|
||||
IconUser,
|
||||
IconArrowBarLeft,
|
||||
IconArrowBarRight,
|
||||
} from '@tabler/icons-react';
|
||||
import {
|
||||
Title,
|
||||
Tooltip,
|
||||
UnstyledButton,
|
||||
ActionIcon,
|
||||
} from '@mantine/core';
|
||||
import { useDisclosure, useMediaQuery } from '@mantine/hooks';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { useEffect, useMemo, 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<string, { label: string; to: string }[]> = {
|
||||
Home: [
|
||||
{ label: 'Inicio', to: '/' },
|
||||
{ label: 'Consulta Api', to: '/Consulta_API' },
|
||||
{ label: 'Prueba_appshell', to: '/prueba_appshell' },
|
||||
],
|
||||
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 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<string | null>(null);
|
||||
|
||||
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) => (
|
||||
<Tooltip
|
||||
label={link.label}
|
||||
position="right"
|
||||
withArrow
|
||||
transitionProps={{ duration: 0 }}
|
||||
key={link.label}
|
||||
>
|
||||
<UnstyledButton
|
||||
onClick={() => {
|
||||
setManualActiveTab(link.label);
|
||||
if (isMobile) closeMobile();
|
||||
}}
|
||||
className={classes.mainLink}
|
||||
data-active={link.label === active || undefined}
|
||||
>
|
||||
<link.icon size={22} stroke={1.5} />
|
||||
</UnstyledButton>
|
||||
</Tooltip>
|
||||
));
|
||||
|
||||
const links = (submenuLinks[active] || []).map((item) => (
|
||||
<Link
|
||||
className={classes.link}
|
||||
data-active={activeLink === item.label || undefined}
|
||||
to={item.to}
|
||||
key={item.label}
|
||||
style={{ display: isCollapsed ? 'none' : 'block' }}
|
||||
onClick={() => {
|
||||
if (isMobile) closeMobile();
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
));
|
||||
|
||||
// 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 (
|
||||
<nav className={`${classes.navbar} ${isCollapsed ? classes.collapsed : ''}`}>
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.aside}>
|
||||
<div className={classes.topSection}>
|
||||
<div className={classes.logo}>
|
||||
<img
|
||||
src="/src/favicon.svg"
|
||||
alt="Logo"
|
||||
style={{ width: 30, height: 30 }}
|
||||
/>
|
||||
</div>
|
||||
{mainLinks}
|
||||
</div>
|
||||
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
onClick={() => (isMobile ? toggleMobile() : toggleDesktop())}
|
||||
className={classes.collapseButton}
|
||||
size="lg"
|
||||
>
|
||||
{isCollapsed ? <IconArrowBarRight size={20} /> : <IconArrowBarLeft size={20} />}
|
||||
</ActionIcon>
|
||||
</div>
|
||||
|
||||
<div className={classes.main}>
|
||||
{!isCollapsed && (
|
||||
<Title order={4} className={classes.title}>
|
||||
{active}
|
||||
</Title>
|
||||
)}
|
||||
{links}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +1,12 @@
|
||||
import { Box, Title, Text, Button, Group, Stack, Image, Center } from '@mantine/core';
|
||||
import { IconArrowLeft } from '@tabler/icons-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { DoubleNavbar } from '../components/DoubleNavbar'; // Ajusta ruta si es necesario
|
||||
import { MantineCardWithShader } from '../components/HoloShader'; // Ajusta ruta si es necesario
|
||||
|
||||
|
||||
export function Error_404() {
|
||||
return (
|
||||
<Box style={{ display: 'flex', height: '100vh' }}>
|
||||
<DoubleNavbar />
|
||||
|
||||
|
||||
|
||||
<Box style={{ flex: 1, display: 'flex', justifyContent: 'center', alignItems: 'center', padding: '2rem' }}>
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Badge,
|
||||
Group,
|
||||
} from '@mantine/core';
|
||||
import { DoubleNavbar } from '../components/DoubleNavbar';
|
||||
import { MetodoSelect } from '../components/MetodoSelect'; // 👈 Importación del nuevo componente
|
||||
import { useEffect } from 'react';
|
||||
|
||||
@@ -88,7 +87,6 @@ export function Consulta_API() {
|
||||
|
||||
return (
|
||||
<Box style={{ display: 'flex', height: '100vh' }}>
|
||||
<DoubleNavbar />
|
||||
|
||||
<Box style={{ flex: 1, padding: 40 }}>
|
||||
<Center style={{ height: '100%' }}>
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ColorSchemeToggle } from '../components/ColorSchemeToggle/ColorSchemeTo
|
||||
import { Welcome } from '../components/Welcome/Welcome';
|
||||
import MiBoton from '../components/botoncito';
|
||||
import { Center, Box } from '@mantine/core';
|
||||
import { DoubleNavbar } from '../components/DoubleNavbar';
|
||||
import { MantineCardWithShader } from '../components/HoloShader';
|
||||
|
||||
|
||||
@@ -11,14 +10,7 @@ import { MantineCardWithShader } from '../components/HoloShader';
|
||||
export function HomePage() {
|
||||
return (
|
||||
<Box style={{ display: 'flex', height: '100vh' }}>
|
||||
<DoubleNavbar /> {/* Sidebar fijo a la izquierda */}
|
||||
|
||||
{/* Contenido principal */}
|
||||
<Box style={{ flex: 1, padding: '24px' }}>
|
||||
<Welcome />
|
||||
<ColorSchemeToggle />
|
||||
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -2,13 +2,12 @@ import { ColorSchemeToggle } from '../components/ColorSchemeToggle/ColorSchemeTo
|
||||
import { Welcome } from '../components/Welcome/Welcome';
|
||||
import MiBoton from '../components/botoncito';
|
||||
import { Center, Box } from '@mantine/core';
|
||||
import { DoubleNavbar } from '../components/DoubleNavbar';
|
||||
import { MantineCardWithShader } from '../components/HoloShader';
|
||||
import { CollapseDesktop } from '../components/Appshell_collapse';
|
||||
import {AppShellWithMenu } from '../components/Appshell_collapse';
|
||||
|
||||
|
||||
export function Prueba_appshell() {
|
||||
return (
|
||||
<CollapseDesktop />
|
||||
<AppShellWithMenu />
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user