feat(agencia): kpis y cartera arriba en portafolio y subpagina panel de control conexiones
This commit is contained in:
43
src/app/dashboard/agencia/conexiones/page.tsx
Normal file
43
src/app/dashboard/agencia/conexiones/page.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
import AgencyPortfolioBoard from '@/components/AgencyPortfolioBoard';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { PageHeader } from '@/components/ui';
|
||||
import { isAgencyRole, userRole } from '@/lib/agency';
|
||||
|
||||
export const revalidate = 0;
|
||||
|
||||
export default async function AgencyConexionesPage() {
|
||||
const supabase = await createClient();
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser();
|
||||
if (!user) redirect('/login');
|
||||
|
||||
if (!isAgencyRole(userRole(user))) {
|
||||
redirect('/dashboard/kpis');
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<Link
|
||||
href="/dashboard/agencia"
|
||||
className="inline-flex items-center gap-1.5 text-xs text-mist-500 hover:text-ink font-medium mb-3 transition-colors"
|
||||
>
|
||||
<ArrowLeft className="w-3.5 h-3.5" />
|
||||
Volver a Portafolio Agencia
|
||||
</Link>
|
||||
<PageHeader
|
||||
eyebrow="Agencia"
|
||||
title="Panel de control · clientes y conexiones"
|
||||
description="Gestión integral de conexiones por cliente (Meta Ads, Google Drive, Superhuman, Clientify), alertas operativas y salud de integraciones."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<AgencyPortfolioBoard />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Building2, DollarSign, Users, TrendingUp } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { Building2, DollarSign, Users, TrendingUp, Plug, ArrowRight } from 'lucide-react';
|
||||
import ClientManagement from '@/components/ClientManagement';
|
||||
import AgencyPortfolioBoard from '@/components/AgencyPortfolioBoard';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { PageHeader } from '@/components/ui';
|
||||
@@ -47,7 +47,7 @@ export default async function AgencyPage() {
|
||||
fetch(`${supabaseUrl}/rest/v1/tenants?select=*&order=created_at.asc`, { headers, cache: 'no-store' }),
|
||||
fetch(`${supabaseUrl}/rest/v1/ad_daily?select=spend`, { headers, cache: 'no-store' }),
|
||||
fetch(`${supabaseUrl}/rest/v1/leads?select=id`, { headers, cache: 'no-store' }),
|
||||
fetch(`${supabaseUrl}/rest/v1/deals?select=amount&stage=eq.won`, { headers, cache: 'no-store' })
|
||||
fetch(`${supabaseUrl}/rest/v1/deals?select=amount&stage=eq.won`, { headers, cache: 'no-store' }),
|
||||
]);
|
||||
|
||||
if (tenantsRes.ok) tenants = await tenantsRes.json();
|
||||
@@ -72,58 +72,66 @@ export default async function AgencyPage() {
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<PageHeader
|
||||
eyebrow="Agencia"
|
||||
title="Portafolio"
|
||||
description="Clientes, conexiones (Meta, Google, Drive, Clientify…) y seguimiento operativo."
|
||||
title="Portafolio Agencia"
|
||||
description="Visión general de clientes activos, métricas globales de inversión y rendimiento de la agencia."
|
||||
/>
|
||||
<Link
|
||||
href="/dashboard/agencia/conexiones"
|
||||
className="inline-flex items-center gap-2 self-start sm:self-auto px-4 py-2.5 rounded-xl border border-gray-200 bg-white text-gray-800 font-semibold text-xs shadow-xs hover:border-brand-500 hover:text-brand-600 transition-colors shrink-0"
|
||||
>
|
||||
<Plug className="w-4 h-4 text-brand-500" />
|
||||
<span>Panel de control · clientes y conexiones</span>
|
||||
<ArrowRight className="w-3.5 h-3.5 text-gray-400" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<AgencyPortfolioBoard />
|
||||
|
||||
{/* Global Agency KPI Cards */}
|
||||
{/* 1. Parte más alta: Global Agency KPI Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-5">
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-5 shadow-xs">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs text-gray-600 font-semibold">Clientes / Tenants</span>
|
||||
<Building2 className="w-4 h-4 text-brand-400" />
|
||||
<span className="text-xs text-gray-600 font-semibold">Clients / Tenants</span>
|
||||
<Building2 className="w-4 h-4 text-brand-500" />
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-gray-900">{tenants.length}</p>
|
||||
<p className="text-[11px] text-emerald-400 mt-1">100% aislados vía RLS</p>
|
||||
<p className="text-[11px] text-emerald-600 mt-1">100% aislados vía RLS</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-5">
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-5 shadow-xs">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs text-gray-600 font-semibold">Inversión Gestionada</span>
|
||||
<DollarSign className="w-4 h-4 text-blue-400" />
|
||||
<span className="text-xs text-gray-600 font-semibold">Inversión gestionada</span>
|
||||
<DollarSign className="w-4 h-4 text-blue-500" />
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-gray-900">
|
||||
{totalSpend.toLocaleString('es-ES', { minimumFractionDigits: 2 })} €
|
||||
</p>
|
||||
<p className="text-[11px] text-gray-400 mt-1">Vía Windsor.ai pipeline</p>
|
||||
<p className="text-[11px] text-gray-500 mt-1">Vía Windsor.ai pipeline</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-5">
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-5 shadow-xs">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs text-gray-600 font-semibold">Leads Captados</span>
|
||||
<Users className="w-4 h-4 text-purple-400" />
|
||||
<span className="text-xs text-gray-600 font-semibold">Leads captados</span>
|
||||
<Users className="w-4 h-4 text-purple-500" />
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-gray-900">{totalLeads}</p>
|
||||
<p className="text-[11px] text-gray-400 mt-1">{totalWonDeals} cerrados ganados</p>
|
||||
<p className="text-[11px] text-gray-500 mt-1">{totalWonDeals} cerrados ganados</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-5">
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-5 shadow-xs">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs text-gray-600 font-semibold">ROAS Blended Agencia</span>
|
||||
<TrendingUp className="w-4 h-4 text-emerald-400" />
|
||||
<span className="text-xs text-gray-600 font-semibold">ROAS blended agencia</span>
|
||||
<TrendingUp className="w-4 h-4 text-emerald-500" />
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-gray-900">{blendedRoas}x</p>
|
||||
<p className="text-[11px] text-emerald-400 mt-1">
|
||||
<p className="text-[11px] text-emerald-600 mt-1">
|
||||
{totalRevenue.toLocaleString('es-ES', { minimumFractionDigits: 2 })} € generados
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Interactive Tenants Management with Onboarding Form */}
|
||||
{/* 2. Debajo: Módulo Cartera de Clientes Activos */}
|
||||
<ClientManagement initialTenants={tenants} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
Sparkles,
|
||||
Images,
|
||||
History,
|
||||
Plug,
|
||||
} from 'lucide-react';
|
||||
import SidebarTenantSwitcher from '@/components/SidebarTenantSwitcher';
|
||||
import type { DashboardModuleId } from '@/lib/tenant-modules';
|
||||
@@ -137,7 +138,7 @@ function shouldShowDateHeader(pathname: string | null): boolean {
|
||||
|
||||
function isActive(pathname: string, href: string): boolean {
|
||||
if (pathname === href) return true;
|
||||
if (href !== '/dashboard' && pathname.startsWith(href + '/')) return true;
|
||||
if (href !== '/dashboard' && href !== '/dashboard/agencia' && pathname.startsWith(href + '/')) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -288,6 +289,15 @@ export default function DashboardShell({
|
||||
}}
|
||||
onNavigate={close}
|
||||
/>
|
||||
<NavLink
|
||||
item={{
|
||||
href: '/dashboard/agencia/conexiones',
|
||||
label: 'Panel de control · clientes y conexiones',
|
||||
icon: Plug,
|
||||
iconClass: 'text-cyan-500',
|
||||
}}
|
||||
onNavigate={close}
|
||||
/>
|
||||
<NavLink
|
||||
item={{
|
||||
href: '/dashboard/historial',
|
||||
|
||||
Reference in New Issue
Block a user