Let agency configure each client's left-nav modules from the ficha.
Checkboxes match the sidebar (incl. Social sub-items), with a live preview and route guard so clients cannot open disabled sections. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,6 +7,7 @@ export type DashboardModuleId =
|
||||
| 'estrategia'
|
||||
| 'marca'
|
||||
| 'calendario'
|
||||
| 'pipeline'
|
||||
| 'ad-tracker'
|
||||
| 'social'
|
||||
| 'organico'
|
||||
@@ -21,6 +22,8 @@ export type DashboardModuleDef = {
|
||||
/** Integration providers that typically unlock this module. */
|
||||
providers: string[];
|
||||
hint: string;
|
||||
/** Shown nested under this parent in the client-menu editor (not a top-level nav row). */
|
||||
parentId?: DashboardModuleId;
|
||||
};
|
||||
|
||||
export const DASHBOARD_MODULES: DashboardModuleDef[] = [
|
||||
@@ -59,13 +62,6 @@ export const DASHBOARD_MODULES: DashboardModuleDef[] = [
|
||||
providers: [],
|
||||
hint: 'Brandbook: voz, oferta, ads, LinkedIn, blog',
|
||||
},
|
||||
{
|
||||
id: 'calendario',
|
||||
href: '/dashboard/calendario',
|
||||
label: 'Calendario Editorial',
|
||||
providers: ['instagram', 'facebook', 'tiktok', 'linkedin'],
|
||||
hint: 'RRSS / contenido',
|
||||
},
|
||||
{
|
||||
id: 'ad-tracker',
|
||||
href: '/dashboard/ad-tracker',
|
||||
@@ -80,6 +76,22 @@ export const DASHBOARD_MODULES: DashboardModuleDef[] = [
|
||||
providers: ['instagram', 'facebook', 'tiktok', 'linkedin'],
|
||||
hint: 'RRSS orgánicas: perfiles, alcance y contenido',
|
||||
},
|
||||
{
|
||||
id: 'calendario',
|
||||
href: '/dashboard/calendario',
|
||||
label: 'Calendario editorial',
|
||||
providers: ['instagram', 'facebook', 'tiktok', 'linkedin'],
|
||||
hint: 'Submenú de Social · planificación RRSS',
|
||||
parentId: 'social',
|
||||
},
|
||||
{
|
||||
id: 'pipeline',
|
||||
href: '/dashboard/creatives',
|
||||
label: 'Pipeline RRSS',
|
||||
providers: ['instagram', 'facebook', 'tiktok', 'linkedin', 'google_drive'],
|
||||
hint: 'Submenú de Social · piezas orgánicas a publicar',
|
||||
parentId: 'social',
|
||||
},
|
||||
{
|
||||
id: 'organico',
|
||||
href: '/dashboard/organico',
|
||||
@@ -148,9 +160,39 @@ export function suggestModulesFromIntegrations(
|
||||
}
|
||||
// Always keep Métricas if any paid/analytics, else still on as home
|
||||
if (!out.kpis) out.kpis = true;
|
||||
// Nested Social tools follow Social when suggested
|
||||
if (out.social) {
|
||||
if (out.calendario === undefined) out.calendario = true;
|
||||
if (out.pipeline === undefined) out.pipeline = true;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Top-level nav modules (exclude nested sub-items). */
|
||||
export function topLevelDashboardModules(): DashboardModuleDef[] {
|
||||
return DASHBOARD_MODULES.filter((m) => !m.parentId);
|
||||
}
|
||||
|
||||
/** Nested toggles under a parent (e.g. Social). */
|
||||
export function childDashboardModules(
|
||||
parentId: DashboardModuleId
|
||||
): DashboardModuleDef[] {
|
||||
return DASHBOARD_MODULES.filter((m) => m.parentId === parentId);
|
||||
}
|
||||
|
||||
/** Resolve module id for a dashboard pathname (for access guards). */
|
||||
export function moduleIdForPath(pathname: string): DashboardModuleId | null {
|
||||
const path = pathname.split('?')[0] || pathname;
|
||||
// Longer hrefs first so /dashboard/creatives wins over /dashboard
|
||||
const ranked = [...DASHBOARD_MODULES].sort(
|
||||
(a, b) => b.href.length - a.href.length
|
||||
);
|
||||
for (const m of ranked) {
|
||||
if (path === m.href || path.startsWith(m.href + '/')) return m.id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function resolveTenantModules(
|
||||
settings: TenantSettings | null | undefined,
|
||||
integrations: { provider: string; status?: string; enabled?: boolean }[]
|
||||
@@ -172,9 +214,10 @@ export function isModuleEnabled(
|
||||
href: string
|
||||
): boolean {
|
||||
if (!modules) return true;
|
||||
const def = DASHBOARD_MODULES.find(
|
||||
(m) => href === m.href || href.startsWith(m.href + '/')
|
||||
);
|
||||
if (!def) return true;
|
||||
return modules[def.id] !== false;
|
||||
const id = moduleIdForPath(href);
|
||||
if (!id) return true;
|
||||
if (modules[id] === false) return false;
|
||||
const def = DASHBOARD_MODULES.find((m) => m.id === id);
|
||||
if (def?.parentId && modules[def.parentId] === false) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user