feat(shell): agrupar secciones CRM, Ads, Social y Organico dentro de Metricas
This commit is contained in:
@@ -61,34 +61,6 @@ const NAV_ITEMS: NavItem[] = [
|
|||||||
iconClass: 'text-brand-500',
|
iconClass: 'text-brand-500',
|
||||||
moduleId: 'kpis',
|
moduleId: 'kpis',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
href: '/dashboard/crm',
|
|
||||||
label: 'CRM',
|
|
||||||
icon: Contact,
|
|
||||||
iconClass: 'text-teal-500',
|
|
||||||
moduleId: 'crm',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/dashboard/ad-tracker',
|
|
||||||
label: 'Ads',
|
|
||||||
icon: Megaphone,
|
|
||||||
iconClass: 'text-violet-500',
|
|
||||||
moduleId: 'ad-tracker',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/dashboard/social',
|
|
||||||
label: 'Social',
|
|
||||||
icon: Share2,
|
|
||||||
iconClass: 'text-fuchsia-500',
|
|
||||||
moduleId: 'social',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/dashboard/organico',
|
|
||||||
label: 'Orgánico',
|
|
||||||
icon: Leaf,
|
|
||||||
iconClass: 'text-lime-600',
|
|
||||||
moduleId: 'organico',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
href: '/dashboard/marca',
|
href: '/dashboard/marca',
|
||||||
label: 'Zona de marca',
|
label: 'Zona de marca',
|
||||||
@@ -266,17 +238,31 @@ export default function DashboardShell({
|
|||||||
pathname === '/dashboard/agencia' ||
|
pathname === '/dashboard/agencia' ||
|
||||||
(pathname !== null && pathname.startsWith('/dashboard/agencia/'));
|
(pathname !== null && pathname.startsWith('/dashboard/agencia/'));
|
||||||
|
|
||||||
|
const isMetricasRoute =
|
||||||
|
pathname === '/dashboard/kpis' ||
|
||||||
|
(pathname !== null && pathname.startsWith('/dashboard/kpis/')) ||
|
||||||
|
pathname === '/dashboard/crm' ||
|
||||||
|
(pathname !== null && pathname.startsWith('/dashboard/crm/')) ||
|
||||||
|
pathname === '/dashboard/ad-tracker' ||
|
||||||
|
(pathname !== null && pathname.startsWith('/dashboard/ad-tracker/')) ||
|
||||||
|
isSocialRoute ||
|
||||||
|
pathname === '/dashboard/organico' ||
|
||||||
|
(pathname !== null && pathname.startsWith('/dashboard/organico/'));
|
||||||
|
|
||||||
const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>({});
|
const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
|
const isMetricasOpen = expandedSections['metricas'] ?? isMetricasRoute;
|
||||||
const isSocialOpen = expandedSections['social'] ?? isSocialRoute;
|
const isSocialOpen = expandedSections['social'] ?? isSocialRoute;
|
||||||
const isAgenciaOpen = expandedSections['agencia'] ?? isAgenciaRoute;
|
const isAgenciaOpen = expandedSections['agencia'] ?? isAgenciaRoute;
|
||||||
|
|
||||||
const toggleSection = (key: 'social' | 'agencia') => {
|
const toggleSection = (key: 'metricas' | 'social' | 'agencia') => {
|
||||||
setExpandedSections((prev) => {
|
setExpandedSections((prev) => {
|
||||||
const current =
|
const current =
|
||||||
key === 'social'
|
key === 'metricas'
|
||||||
? (prev['social'] ?? isSocialRoute)
|
? (prev['metricas'] ?? isMetricasRoute)
|
||||||
: (prev['agencia'] ?? isAgenciaRoute);
|
: key === 'social'
|
||||||
|
? (prev['social'] ?? isSocialRoute)
|
||||||
|
: (prev['agencia'] ?? isAgenciaRoute);
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[key]: !current,
|
[key]: !current,
|
||||||
@@ -290,7 +276,20 @@ export default function DashboardShell({
|
|||||||
return enabledModules[id] !== false;
|
return enabledModules[id] !== false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const visibleNav = NAV_ITEMS.filter((item) => moduleOn(item.moduleId));
|
const isItemVisible = (item: NavItem) => {
|
||||||
|
if (item.moduleId === 'kpis') {
|
||||||
|
return (
|
||||||
|
moduleOn('kpis') ||
|
||||||
|
moduleOn('crm') ||
|
||||||
|
moduleOn('ad-tracker') ||
|
||||||
|
moduleOn('social') ||
|
||||||
|
moduleOn('organico')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return moduleOn(item.moduleId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const visibleNav = NAV_ITEMS.filter(isItemVisible);
|
||||||
|
|
||||||
const subLinkCls = (href: string) =>
|
const subLinkCls = (href: string) =>
|
||||||
`ml-7 flex items-center gap-2.5 rounded-md px-3 py-1.5 text-[13px] font-medium transition-colors ${
|
`ml-7 flex items-center gap-2.5 rounded-md px-3 py-1.5 text-[13px] font-medium transition-colors ${
|
||||||
@@ -322,40 +321,152 @@ export default function DashboardShell({
|
|||||||
|
|
||||||
<nav className="flex-1 min-h-0 space-y-0.5 overflow-y-auto pr-1">
|
<nav className="flex-1 min-h-0 space-y-0.5 overflow-y-auto pr-1">
|
||||||
{visibleNav.map((item) => {
|
{visibleNav.map((item) => {
|
||||||
// Sub-enlaces de Social: siempre que Social esté en el menú.
|
const underMetricas = item.moduleId === 'kpis';
|
||||||
const underSocial = item.moduleId === 'social';
|
|
||||||
return (
|
return (
|
||||||
<div key={item.href} className="space-y-0.5">
|
<div key={item.href} className="space-y-0.5">
|
||||||
<NavLink
|
<NavLink
|
||||||
item={item}
|
item={item}
|
||||||
hasSubItems={underSocial}
|
hasSubItems={underMetricas}
|
||||||
isOpen={isSocialOpen}
|
isOpen={isMetricasOpen}
|
||||||
onToggle={() => toggleSection('social')}
|
onToggle={() => toggleSection('metricas')}
|
||||||
onNavigate={close}
|
onNavigate={close}
|
||||||
/>
|
/>
|
||||||
{underSocial && isSocialOpen ? (
|
{underMetricas && isMetricasOpen ? (
|
||||||
<>
|
<div className="space-y-0.5 my-0.5">
|
||||||
{moduleOn('calendario') ? (
|
{moduleOn('crm') ? (
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard/calendario"
|
href="/dashboard/crm"
|
||||||
onClick={close}
|
onClick={close}
|
||||||
className={subLinkCls('/dashboard/calendario')}
|
className={subLinkCls('/dashboard/crm')}
|
||||||
>
|
>
|
||||||
<CalendarDays className="h-3.5 w-3.5 shrink-0" />
|
<Contact
|
||||||
Calendario editorial
|
className={`h-3.5 w-3.5 shrink-0 ${
|
||||||
|
isActive(pathname, '/dashboard/crm')
|
||||||
|
? 'text-teal-400'
|
||||||
|
: 'text-mist-400 group-hover:text-mist-300'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
<span className="truncate">CRM</span>
|
||||||
</Link>
|
</Link>
|
||||||
) : null}
|
) : null}
|
||||||
{moduleOn('pipeline') ? (
|
|
||||||
|
{moduleOn('ad-tracker') ? (
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard/creatives"
|
href="/dashboard/ad-tracker"
|
||||||
onClick={close}
|
onClick={close}
|
||||||
className={subLinkCls('/dashboard/creatives')}
|
className={subLinkCls('/dashboard/ad-tracker')}
|
||||||
>
|
>
|
||||||
<Images className="h-3.5 w-3.5 shrink-0" />
|
<Megaphone
|
||||||
Pipeline RRSS
|
className={`h-3.5 w-3.5 shrink-0 ${
|
||||||
|
isActive(pathname, '/dashboard/ad-tracker')
|
||||||
|
? 'text-violet-400'
|
||||||
|
: 'text-mist-400 group-hover:text-mist-300'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
<span className="truncate">Ads</span>
|
||||||
</Link>
|
</Link>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
|
||||||
|
{moduleOn('social') ? (
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<Link
|
||||||
|
href="/dashboard/social"
|
||||||
|
onClick={(e) => {
|
||||||
|
if (moduleOn('calendario') || moduleOn('pipeline')) {
|
||||||
|
if (pathname === '/dashboard/social') {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleSection('social');
|
||||||
|
return;
|
||||||
|
} else if (!isSocialOpen) {
|
||||||
|
toggleSection('social');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
}}
|
||||||
|
className={`${subLinkCls('/dashboard/social')} justify-between`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2.5 min-w-0">
|
||||||
|
<Share2
|
||||||
|
className={`h-3.5 w-3.5 shrink-0 ${
|
||||||
|
isActive(pathname, '/dashboard/social')
|
||||||
|
? 'text-fuchsia-400'
|
||||||
|
: 'text-mist-400 group-hover:text-mist-300'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
<span className="truncate">Social</span>
|
||||||
|
</div>
|
||||||
|
{moduleOn('calendario') || moduleOn('pipeline') ? (
|
||||||
|
<span
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleSection('social');
|
||||||
|
}}
|
||||||
|
className="p-0.5 rounded hover:bg-white/10 text-mist-400 hover:text-white transition-colors shrink-0"
|
||||||
|
aria-label={isSocialOpen ? 'Ocultar páginas' : 'Mostrar páginas'}
|
||||||
|
>
|
||||||
|
<ChevronDown
|
||||||
|
className={`h-3 w-3 transition-transform duration-200 ${
|
||||||
|
isSocialOpen ? 'rotate-0 text-white' : '-rotate-90 text-mist-400'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</Link>
|
||||||
|
{isSocialOpen ? (
|
||||||
|
<div className="space-y-0.5 my-0.5">
|
||||||
|
{moduleOn('calendario') ? (
|
||||||
|
<Link
|
||||||
|
href="/dashboard/calendario"
|
||||||
|
onClick={close}
|
||||||
|
className={`ml-11 flex items-center gap-2 rounded-md px-2.5 py-1.5 text-[12px] font-medium transition-colors ${
|
||||||
|
isActive(pathname, '/dashboard/calendario')
|
||||||
|
? 'bg-white/[0.07] text-white'
|
||||||
|
: 'text-mist-400 hover:bg-white/[0.05] hover:text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<CalendarDays className="h-3 w-3 shrink-0" />
|
||||||
|
<span className="truncate">Calendario editorial</span>
|
||||||
|
</Link>
|
||||||
|
) : null}
|
||||||
|
{moduleOn('pipeline') ? (
|
||||||
|
<Link
|
||||||
|
href="/dashboard/creatives"
|
||||||
|
onClick={close}
|
||||||
|
className={`ml-11 flex items-center gap-2 rounded-md px-2.5 py-1.5 text-[12px] font-medium transition-colors ${
|
||||||
|
isActive(pathname, '/dashboard/creatives')
|
||||||
|
? 'bg-white/[0.07] text-white'
|
||||||
|
: 'text-mist-400 hover:bg-white/[0.05] hover:text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Images className="h-3 w-3 shrink-0" />
|
||||||
|
<span className="truncate">Pipeline RRSS</span>
|
||||||
|
</Link>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{moduleOn('organico') ? (
|
||||||
|
<Link
|
||||||
|
href="/dashboard/organico"
|
||||||
|
onClick={close}
|
||||||
|
className={subLinkCls('/dashboard/organico')}
|
||||||
|
>
|
||||||
|
<Leaf
|
||||||
|
className={`h-3.5 w-3.5 shrink-0 ${
|
||||||
|
isActive(pathname, '/dashboard/organico')
|
||||||
|
? 'text-lime-400'
|
||||||
|
: 'text-mist-400 group-hover:text-mist-300'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
<span className="truncate">Orgánico</span>
|
||||||
|
</Link>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user