feat: metrics dashboard with WoW/MoM/YoY and creatives pipeline
Rebuild KPIs as a real dashboard (period deltas, tenant zoom), clarify objetivos/calendario/activos sources, and turn creatives into a status pipeline with delete. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
70
src/lib/tenant-context.ts
Normal file
70
src/lib/tenant-context.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { cookies } from 'next/headers';
|
||||
import {
|
||||
VIEW_TENANT_COOKIE,
|
||||
VIEW_TENANT_NAME_COOKIE,
|
||||
homeTenantId,
|
||||
isAgencyRole,
|
||||
serviceHeaders,
|
||||
supabaseUrl,
|
||||
userRole,
|
||||
} from '@/lib/agency';
|
||||
|
||||
export type ResolvedTenant = {
|
||||
tenantId: string | null;
|
||||
/** true = agency zoomed into one client */
|
||||
viaView: boolean;
|
||||
/** true = agency with no client selected → all tenants */
|
||||
portfolioWide: boolean;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export async function resolveActiveTenant(user: {
|
||||
app_metadata?: Record<string, unknown>;
|
||||
user_metadata?: Record<string, unknown>;
|
||||
} | null): Promise<ResolvedTenant> {
|
||||
const role = userRole(user);
|
||||
const cookieStore = await cookies();
|
||||
const viewId = cookieStore.get(VIEW_TENANT_COOKIE)?.value || null;
|
||||
const viewName = cookieStore.get(VIEW_TENANT_NAME_COOKIE)?.value || null;
|
||||
|
||||
if (isAgencyRole(role) && viewId) {
|
||||
return {
|
||||
tenantId: viewId,
|
||||
viaView: true,
|
||||
portfolioWide: false,
|
||||
label: viewName || 'Cliente',
|
||||
};
|
||||
}
|
||||
if (isAgencyRole(role) && !viewId) {
|
||||
return {
|
||||
tenantId: null,
|
||||
viaView: false,
|
||||
portfolioWide: true,
|
||||
label: 'Portafolio (todos los clientes)',
|
||||
};
|
||||
}
|
||||
return {
|
||||
tenantId: homeTenantId(user),
|
||||
viaView: false,
|
||||
portfolioWide: false,
|
||||
label:
|
||||
(user?.user_metadata?.company as string) ||
|
||||
(user?.app_metadata?.company as string) ||
|
||||
'Cliente',
|
||||
};
|
||||
}
|
||||
|
||||
export async function restGet<T = unknown>(
|
||||
path: string
|
||||
): Promise<{ ok: boolean; data: T | null; error?: string }> {
|
||||
const headers = serviceHeaders();
|
||||
if (!headers) return { ok: false, data: null, error: 'no service role' };
|
||||
const r = await fetch(`${supabaseUrl()}${path}`, {
|
||||
headers,
|
||||
cache: 'no-store',
|
||||
});
|
||||
if (!r.ok) {
|
||||
return { ok: false, data: null, error: await r.text() };
|
||||
}
|
||||
return { ok: true, data: (await r.json()) as T };
|
||||
}
|
||||
Reference in New Issue
Block a user