feat: agency portfolio with per-client integrations hub
Admin home lists tenants, alerts, and connection health; header selector enables superadmin context; Meta/GA4/GSC/GTM/CRM/email slots live per tenant (manual status until OAuth). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
66
src/lib/agency.ts
Normal file
66
src/lib/agency.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/** Agency portfolio helpers — providers, role gates, service-role REST. */
|
||||
|
||||
export const INTEGRATION_PROVIDERS = [
|
||||
{ id: 'meta_ads', label: 'Meta Ads', group: 'paid' },
|
||||
{ id: 'google_ads', label: 'Google Ads (MCC)', group: 'paid' },
|
||||
{ id: 'ga4', label: 'Google Analytics 4', group: 'analytics' },
|
||||
{ id: 'search_console', label: 'Search Console', group: 'analytics' },
|
||||
{ id: 'tag_manager', label: 'Tag Manager', group: 'analytics' },
|
||||
{ id: 'clientify', label: 'Clientify', group: 'crm' },
|
||||
{ id: 'klaviyo', label: 'Klaviyo', group: 'email' },
|
||||
{ id: 'mailchimp', label: 'Mailchimp', group: 'email' },
|
||||
] as const;
|
||||
|
||||
export type IntegrationProviderId =
|
||||
(typeof INTEGRATION_PROVIDERS)[number]['id'];
|
||||
|
||||
export type IntegrationStatus =
|
||||
| 'disconnected'
|
||||
| 'pending'
|
||||
| 'connected'
|
||||
| 'error';
|
||||
|
||||
export function isAgencyRole(role: string | undefined | null): boolean {
|
||||
return role === 'admin' || role === 'team';
|
||||
}
|
||||
|
||||
export function userRole(user: {
|
||||
app_metadata?: Record<string, unknown>;
|
||||
user_metadata?: Record<string, unknown>;
|
||||
} | null): string {
|
||||
return (
|
||||
(user?.app_metadata?.role as string) ||
|
||||
(user?.user_metadata?.role as string) ||
|
||||
'client'
|
||||
);
|
||||
}
|
||||
|
||||
export function homeTenantId(user: {
|
||||
app_metadata?: Record<string, unknown>;
|
||||
user_metadata?: Record<string, unknown>;
|
||||
} | null): string | null {
|
||||
return (
|
||||
(user?.app_metadata?.tenant_id as string) ||
|
||||
(user?.user_metadata?.tenant_id as string) ||
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
export function serviceHeaders():
|
||||
| { apikey: string; Authorization: string }
|
||||
| null {
|
||||
const key = process.env.SUPABASE_SERVICE_ROLE_KEY?.trim();
|
||||
if (!key) return null;
|
||||
return { apikey: key, Authorization: `Bearer ${key}` };
|
||||
}
|
||||
|
||||
export function supabaseUrl(): string {
|
||||
return (
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL?.trim() ||
|
||||
'https://supabase.4ventures.es'
|
||||
);
|
||||
}
|
||||
|
||||
export const VIEW_TENANT_COOKIE = '4v_view_tenant_id';
|
||||
export const VIEW_TENANT_SLUG_COOKIE = '4v_view_tenant_slug';
|
||||
export const VIEW_TENANT_NAME_COOKIE = '4v_view_tenant_name';
|
||||
Reference in New Issue
Block a user