feat: ficha por cliente en portafolio (conexiones, accesos, logo, módulos)
Cada tenant tiene una ficha con plataformas, invitaciones por email, logo de marca y toggles de módulos visibles en el portal cliente. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
151
src/lib/tenant-modules.ts
Normal file
151
src/lib/tenant-modules.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
/** Dashboard modules toggled per tenant (client portal nav). */
|
||||
|
||||
export type DashboardModuleId =
|
||||
| 'kpis'
|
||||
| 'crm'
|
||||
| 'seguimiento'
|
||||
| 'estrategia'
|
||||
| 'calendario'
|
||||
| 'ad-tracker'
|
||||
| 'activos'
|
||||
| 'creatives'
|
||||
| 'asistente';
|
||||
|
||||
export type DashboardModuleDef = {
|
||||
id: DashboardModuleId;
|
||||
href: string;
|
||||
label: string;
|
||||
/** Integration providers that typically unlock this module. */
|
||||
providers: string[];
|
||||
hint: string;
|
||||
};
|
||||
|
||||
export const DASHBOARD_MODULES: DashboardModuleDef[] = [
|
||||
{
|
||||
id: 'kpis',
|
||||
href: '/dashboard/kpis',
|
||||
label: 'Métricas',
|
||||
providers: ['meta_ads', 'google_ads', 'ga4'],
|
||||
hint: 'KPIs de inversión y leads',
|
||||
},
|
||||
{
|
||||
id: 'crm',
|
||||
href: '/dashboard/crm',
|
||||
label: 'CRM',
|
||||
providers: ['clientify'],
|
||||
hint: 'Clientify / leads',
|
||||
},
|
||||
{
|
||||
id: 'seguimiento',
|
||||
href: '/dashboard/seguimiento',
|
||||
label: 'Seguimiento',
|
||||
providers: ['superhuman_docs'],
|
||||
hint: 'Actas y seguimiento operativo',
|
||||
},
|
||||
{
|
||||
id: 'estrategia',
|
||||
href: '/dashboard/estrategia',
|
||||
label: 'Objetivos del Mes',
|
||||
providers: [],
|
||||
hint: 'Targets y presupuesto',
|
||||
},
|
||||
{
|
||||
id: 'calendario',
|
||||
href: '/dashboard/calendario',
|
||||
label: 'Calendario Editorial',
|
||||
providers: ['instagram', 'facebook', 'tiktok', 'linkedin'],
|
||||
hint: 'RRSS / contenido',
|
||||
},
|
||||
{
|
||||
id: 'ad-tracker',
|
||||
href: '/dashboard/ad-tracker',
|
||||
label: 'Ad Tracker',
|
||||
providers: ['meta_ads', 'google_ads'],
|
||||
hint: 'Creatividades Meta / Google',
|
||||
},
|
||||
{
|
||||
id: 'activos',
|
||||
href: '/dashboard/activos',
|
||||
label: 'Biblioteca de Activos',
|
||||
providers: ['google_drive'],
|
||||
hint: 'Drive y archivos',
|
||||
},
|
||||
{
|
||||
id: 'creatives',
|
||||
href: '/dashboard/creatives',
|
||||
label: 'Pipeline creatividades',
|
||||
providers: ['google_drive', 'meta_ads', 'google_ads'],
|
||||
hint: 'Flujo creativo',
|
||||
},
|
||||
{
|
||||
id: 'asistente',
|
||||
href: '/dashboard/asistente',
|
||||
label: 'Asistente IA Financiero',
|
||||
providers: [],
|
||||
hint: 'Consultas sobre cifras',
|
||||
},
|
||||
];
|
||||
|
||||
export type TenantModuleMap = Partial<Record<DashboardModuleId, boolean>>;
|
||||
|
||||
export type TenantSettings = {
|
||||
modules?: TenantModuleMap;
|
||||
/** When true, modules were explicitly saved by agency (don't auto-overwrite). */
|
||||
modulesLocked?: boolean;
|
||||
};
|
||||
|
||||
export function defaultModulesAllOn(): Record<DashboardModuleId, boolean> {
|
||||
const out = {} as Record<DashboardModuleId, boolean>;
|
||||
for (const m of DASHBOARD_MODULES) out[m.id] = true;
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Suggest modules from connected + enabled integrations. */
|
||||
export function suggestModulesFromIntegrations(
|
||||
integrations: { provider: string; status?: string; enabled?: boolean }[]
|
||||
): Record<DashboardModuleId, boolean> {
|
||||
const connected = new Set(
|
||||
integrations
|
||||
.filter((i) => i.enabled !== false && i.status === 'connected')
|
||||
.map((i) => i.provider)
|
||||
);
|
||||
const out = {} as Record<DashboardModuleId, boolean>;
|
||||
for (const m of DASHBOARD_MODULES) {
|
||||
if (!m.providers.length) {
|
||||
out[m.id] = true;
|
||||
continue;
|
||||
}
|
||||
out[m.id] = m.providers.some((p) => connected.has(p));
|
||||
}
|
||||
// Always keep Métricas if any paid/analytics, else still on as home
|
||||
if (!out.kpis) out.kpis = true;
|
||||
return out;
|
||||
}
|
||||
|
||||
export function resolveTenantModules(
|
||||
settings: TenantSettings | null | undefined,
|
||||
integrations: { provider: string; status?: string; enabled?: boolean }[]
|
||||
): Record<DashboardModuleId, boolean> {
|
||||
if (settings?.modulesLocked && settings.modules) {
|
||||
return { ...defaultModulesAllOn(), ...settings.modules };
|
||||
}
|
||||
if (settings?.modules && Object.keys(settings.modules).length > 0) {
|
||||
return { ...defaultModulesAllOn(), ...settings.modules };
|
||||
}
|
||||
if (integrations.length) {
|
||||
return suggestModulesFromIntegrations(integrations);
|
||||
}
|
||||
return defaultModulesAllOn();
|
||||
}
|
||||
|
||||
export function isModuleEnabled(
|
||||
modules: Record<DashboardModuleId, boolean> | null | undefined,
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user