Prechádzať zdrojové kódy

add the final screen, need to ajust

gdias 1 mesiac pred
rodič
commit
834413381e

+ 74 - 0
src/lib/core/models/mock-data.js

@@ -204,3 +204,77 @@ export const mockAspectsDrilldown = {
         ]
     }
 };
+
+export const mockPersonasKpis = {
+    ativas: 5,
+    riscoChurn: '60,1',
+    perdaMensalEst: 'R$ 7.521,27',
+    potencialExpansao: 'Neutro'
+};
+
+export const mockPersonas = [
+    {
+        id: '1',
+        nome: 'Cliente Tristeza Invisível',
+        tipo: 'O PERFIL',
+        descricao: 'Sem conexão com CRM',
+        detalhes: 'Cliente antigo, compras esporádicas. Apresenta sinais de distanciamento e não responde às últimas interações.',
+        expansao: 'Oferecer um atendimento personalizado focando em peças de coleção anterior com desconto exclusivo. Mostrar novidades alinhadas ao histórico de compras.',
+        engajamento: 'Acionar contato humano imediato. Entender o motivo do afastamento oferecendo um benefício de retorno (ex: frete grátis ou cupom surpresa).',
+        risco: 'Alto'
+    },
+    {
+        id: '2',
+        nome: 'Cliente novo lv2 - primeiras compras',
+        tipo: 'O PERFIL',
+        descricao: 'Ainda na fase de experimentação.',
+        detalhes: 'Cliente recente, perfil neutro. Fez de 1 a 2 compras recentemente e está avaliando a marca.',
+        expansao: 'Recomendar produtos complementares aos recém-adquiridos. Enviar conteúdo educativo sobre como usar/combinar os produtos.',
+        engajamento: 'Enviar pesquisa de satisfação simples com incentivo para a próxima compra. Garantir que não haja dúvidas sobre troca/devolução.',
+        risco: 'Baixo'
+    },
+    {
+        id: '3',
+        nome: 'Cliente Sorriso Raro',
+        tipo: 'O PERFIL',
+        descricao: 'Cadastro antigo, mas compras esporádicas.',
+        detalhes: 'Cliente que compra apenas em promoções ou datas comemorativas específicas.',
+        expansao: 'Apresentar campanhas antecipadas de datas festivas. Criar senso de exclusividade VIP.',
+        engajamento: 'Manter no radar com newsletter quinzenal, sem forçar venda direta. Usar gatilhos emocionais nas comunicações.',
+        risco: 'Médio'
+    },
+    {
+        id: '4',
+        nome: 'Cliente Rejeição Invisível',
+        tipo: 'O PERFIL',
+        descricao: 'Sem conexão com CRM',
+        detalhes: 'Cliente que interage, pergunta preços, mas raramente converte nos últimos 6 meses.',
+        expansao: 'Focar em quebra de objeções (prova social, reviews de outros clientes). Oferecer opções de menor ticket de entrada.',
+        engagement: [
+            "Realizar follow-up focado em benefícios práticos do produto para sua rotina",
+            "Disponibilizar material de apoio rápido (como tutoriais curtos em vídeo)"
+        ],
+        riscoChurn: "Baixo",
+        corRisco: "bg-emerald-500"
+    }
+];
+
+export const mockEvolucaoSentimentosData = [
+    { date: new Date(2024, 3, 19), value: 0.05 },
+    { date: new Date(2024, 3, 20), value: 0.08 },
+    { date: new Date(2024, 3, 21), value: 0.06 },
+    { date: new Date(2024, 3, 22), value: 0.1 },
+    { date: new Date(2024, 3, 23), value: 0.12 },
+    { date: new Date(2024, 3, 24), value: 0.09 },
+    { date: new Date(2024, 3, 25), value: 0.11 },
+];
+
+export const mockMonitoramentoPlaybooksData = [
+    { date: new Date(2024, 3, 19), novos: 5, convertidos: 1 },
+    { date: new Date(2024, 3, 20), novos: 15, convertidos: 3 },
+    { date: new Date(2024, 3, 21), novos: 48, convertidos: 10 },
+    { date: new Date(2024, 3, 22), novos: 18, convertidos: 4 },
+    { date: new Date(2024, 3, 23), novos: 22, convertidos: 5 },
+    { date: new Date(2024, 3, 24), novos: 25, convertidos: 6 },
+    { date: new Date(2024, 3, 25), novos: 12, convertidos: 2 },
+];

+ 32 - 4
src/lib/features/sentiment/domain/sentiment-dashboard.service.js

@@ -1,10 +1,35 @@
 import { sentimentDashboardMockSource } from '../data/sentiment-dashboard.mock.js';
+import { AlertTriangle, CheckCircle, MessageSquare, TrendingUp } from 'lucide-svelte';
 
 const summaryCardsConfig = [
-	{ key: 'atRiskClients', label: 'Clientes em risco' },
-	{ key: 'opportunities', label: 'Oportunidades' },
-	{ key: 'recentInteractions', label: 'Interacoes recentes' },
-	{ key: 'netTrend', label: 'Tendencia liquida' }
+	{ 
+		key: 'atRiskClients', 
+		label: 'Clientes em risco',
+		icon: AlertTriangle,
+		color: 'text-red-600 dark:text-red-400',
+		bg: 'bg-red-50 dark:bg-red-400/10'
+	},
+	{ 
+		key: 'opportunities', 
+		label: 'Oportunidades',
+		icon: CheckCircle,
+		color: 'text-emerald-600 dark:text-emerald-400',
+		bg: 'bg-emerald-50 dark:bg-emerald-400/10'
+	},
+	{ 
+		key: 'recentInteractions', 
+		label: 'Interacoes recentes',
+		icon: MessageSquare,
+		color: 'text-sky-600 dark:text-sky-400',
+		bg: 'bg-sky-50 dark:bg-sky-400/10'
+	},
+	{ 
+		key: 'netTrend', 
+		label: 'Tendencia liquida',
+		icon: TrendingUp,
+		color: 'text-indigo-600 dark:text-indigo-400',
+		bg: 'bg-indigo-50 dark:bg-indigo-400/10'
+	}
 ];
 
 const priorityLabel = {
@@ -123,6 +148,9 @@ function mapSummaryCards(summary) {
 		id: config.key,
 		label: config.label,
 		value: summary[config.key]?.value ?? summary[config.key],
+		icon: config.icon,
+		color: config.color,
+		bg: config.bg,
 		image: summary[config.key]?.image ?? null
 	}));
 }

+ 14 - 9
src/lib/features/sentiment/ui/SummaryCards.svelte

@@ -9,25 +9,30 @@
 
 	<div class="grid grid-cols-2 gap-4 xl:grid-cols-4">
 		{#each cards as card}
+			{@const Icon = card.icon}
 			<button
 				type="button"
-				class={`w-full rounded-xl border p-4 text-left shadow-sm transition-all duration-200 ${
+				class={`w-full rounded-xl border p-5 text-left shadow-sm transition-all duration-200 ${
 					selectedCardId === card.id
 						? 'border-indigo-300 bg-indigo-50 dark:border-indigo-500/50 dark:bg-indigo-500/10 ring-1 ring-indigo-200 dark:ring-indigo-500/20'
 						: 'border-slate-200 bg-white hover:border-slate-300 hover:bg-slate-50 dark:border-slate-800 dark:bg-[#1e293b] dark:hover:border-slate-700 dark:hover:bg-slate-800/80'
 				}`}
 				onclick={() => onCardSelect(card)}
 			>
-				<div class="flex items-start justify-between gap-3">
-					<div>
-						<p class="text-3xl leading-none font-extrabold text-slate-900 dark:text-white transition-colors">{card.value}</p>
-						<p class="mt-2 text-xs font-semibold text-slate-500 dark:text-slate-400 transition-colors">{card.label}</p>
-					</div>
-					{#if card.image}
-						<div class="h-10 w-10 shrink-0 rounded-lg border border-slate-200 dark:border-slate-700/50 bg-slate-50 dark:bg-slate-900/50 p-2 flex items-center justify-center transition-colors">
-							<img src={card.image} alt={card.label} class="h-full w-full object-contain opacity-80 dark:opacity-100" />
+				<div class="flex items-start gap-4">
+					{#if Icon}
+						<div class={`h-10 w-10 shrink-0 rounded-lg ${card.bg || 'bg-slate-50 dark:bg-slate-900/50'} ${card.color || 'text-slate-600 dark:text-slate-400'} flex items-center justify-center transition-colors`}>
+							<Icon size={20} strokeWidth={2.5} />
 						</div>
 					{/if}
+					<div>
+						<div class="mb-1 text-xs font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400 transition-colors">
+							{card.label}
+						</div>
+						<div class="mb-0.5 text-2xl font-bold text-slate-900 dark:text-white transition-colors">
+							{card.value}
+						</div>
+					</div>
 				</div>
 			</button>
 		{/each}

+ 89 - 4
src/routes/(app)/+layout.svelte

@@ -1,6 +1,6 @@
 <script>
     import { page } from '$app/stores';
-    import { LayoutDashboard, MessageSquare, BarChart2, Settings, LogOut, Bell, Search, Menu, X, TrendingUp, ChevronLeft, ChevronRight, Moon, Sun } from 'lucide-svelte';
+    import { LayoutDashboard, MessageSquare, BarChart2, Settings, LogOut, Bell, Search, Menu, X, TrendingUp, ChevronLeft, ChevronRight, Moon, Sun, UserRound, User, Key, CreditCard, HelpCircle } from 'lucide-svelte';
     import { goto } from '$app/navigation';
     import logoWhite from '$lib/assets/images/nettown_white_logo.svg';
     import logoBlack from '$lib/assets/images/nettown_black_logo.svg.svg';
@@ -11,6 +11,7 @@
     let isMobileMenuOpen = $state(false);
     let isSidebarCollapsed = $state(false);
     let currentTheme = $state('light');
+    let isUserMenuOpen = $state(false);
 
     onMount(() => {
         theme.subscribe(val => {
@@ -22,6 +23,8 @@
         { name: 'Visão Geral', href: '/dashboard', icon: LayoutDashboard },
         { name: 'Interações', href: '/dashboard/interactions', icon: MessageSquare },
         { name: 'Análise de Sentimento', href: '/dashboard/analytics', icon: BarChart2 },
+        { name: 'Personas', href: '/dashboard/personas', icon: UserRound },
+        { name: 'Evolução', href: '/dashboard/evolucao', icon: TrendingUp },
         { name: 'Configurações', href: '/dashboard/settings', icon: Settings },
     ];
 
@@ -36,6 +39,35 @@
     function toggleSidebar() {
         isSidebarCollapsed = !isSidebarCollapsed;
     }
+
+    function toggleUserMenu() {
+        isUserMenuOpen = !isUserMenuOpen;
+    }
+
+    function handleUserAction(action) {
+        isUserMenuOpen = false;
+        switch(action) {
+            case 'profile':
+                // TODO: Navigate to profile page
+                console.log('Navigate to profile');
+                break;
+            case 'password':
+                // TODO: Open password change modal
+                console.log('Open password change modal');
+                break;
+            case 'billing':
+                // TODO: Navigate to billing page
+                console.log('Navigate to billing');
+                break;
+            case 'help':
+                // TODO: Open help modal
+                console.log('Open help modal');
+                break;
+            case 'logout':
+                handleLogout();
+                break;
+        }
+    }
 </script>
 
 <div class="h-screen w-screen bg-slate-50 dark:bg-[#0f172a] text-slate-900 dark:text-slate-300 font-sans flex overflow-hidden transition-colors duration-200">
@@ -44,6 +76,11 @@
         <div class="fixed inset-0 bg-slate-950/80 z-40 lg:hidden" onclick={toggleMobileMenu} onkeydown={(e) => e.key === 'Escape' && toggleMobileMenu()} tabindex="0" role="button" aria-label="Fechar menu"></div>
     {/if}
 
+    <!-- User Menu Overlay -->
+    {#if isUserMenuOpen}
+        <div class="fixed inset-0 z-40" onclick={() => isUserMenuOpen = false} onkeydown={(e) => e.key === 'Escape' && (isUserMenuOpen = false)} tabindex="0" role="button" aria-label="Fechar menu"></div>
+    {/if}
+
     <!-- Sidebar -->
     <aside class="fixed lg:static inset-y-0 left-0 z-50 h-full bg-white dark:bg-[#1e293b] border-r border-slate-200 dark:border-slate-800 flex flex-col transition-all duration-300 ease-in-out {isSidebarCollapsed ? 'w-20' : 'w-64'} {isMobileMenuOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'} shrink-0 shadow-sm dark:shadow-none">
         <div class="h-16 flex items-center justify-between px-4 border-b border-slate-200 dark:border-slate-800 shrink-0">
@@ -141,13 +178,61 @@
                     <span class="absolute top-1.5 right-1.5 w-2 h-2 rounded-full bg-red-500 border-2 border-white dark:border-[#1e293b]"></span>
                 </button>
                 <div class="h-8 w-px bg-slate-200 dark:bg-slate-700 mx-1"></div>
-                <div class="flex items-center gap-3">
+                <div class="flex items-center gap-3 relative">
                     <div class="text-right hidden sm:block">
                         <div class="text-sm font-medium text-slate-900 dark:text-white">Admin</div>
                         <div class="text-xs text-slate-500">Gestor de Vendas</div>
                     </div>
-                    <div class="w-9 h-9 rounded-full bg-indigo-600 flex items-center justify-center text-white font-bold text-sm shadow-sm shadow-indigo-900/20">
-                        AD
+                    <div class="relative">
+                        <button 
+                            onclick={toggleUserMenu}
+                            class="w-9 h-9 rounded-full bg-indigo-600 flex items-center justify-center text-white font-bold text-sm shadow-sm shadow-indigo-900/20 hover:bg-indigo-700 transition-colors"
+                            title="Menu do usuário"
+                        >
+                            AD
+                        </button>
+                        
+                        <!-- User Dropdown Menu -->
+                        {#if isUserMenuOpen}
+                            <div class="absolute right-0 mt-2 w-56 bg-white dark:bg-[#1e293b] rounded-lg border border-slate-200 dark:border-slate-800 shadow-lg py-1 z-50">
+                                <div class="px-4 py-3 border-b border-slate-200 dark:border-slate-800">
+                                    <p class="text-sm font-medium text-slate-900 dark:text-white">Admin</p>
+                                    <p class="text-xs text-slate-500 dark:text-slate-400">admin@nettown.com</p>
+                                </div>
+                                
+                                <button 
+                                    onclick={() => handleUserAction('profile')}
+                                    class="w-full flex items-center gap-3 px-4 py-2 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors"
+                                >
+                                    <User size={16} />
+                                    Meu Perfil
+                                </button>
+                                
+                                <button 
+                                    onclick={() => handleUserAction('password')}
+                                    class="w-full flex items-center gap-3 px-4 py-2 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors"
+                                >
+                                    <Key size={16} />
+                                    Alterar Senha
+                                </button>
+                                
+                                <button 
+                                    onclick={() => handleUserAction('billing')}
+                                    class="w-full flex items-center gap-3 px-4 py-2 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors"
+                                >
+                                    <CreditCard size={16} />
+                                    Assinatura
+                                </button>
+                                
+                                <button 
+                                    onclick={() => handleUserAction('help')}
+                                    class="w-full flex items-center gap-3 px-4 py-2 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors"
+                                >
+                                    <HelpCircle size={16} />
+                                    Ajuda
+                                </button>
+                            </div>
+                        {/if}
                     </div>
                 </div>
             </div>

+ 215 - 0
src/routes/(app)/dashboard/evolucao/+page.svelte

@@ -0,0 +1,215 @@
+<script>
+	import { TrendingUp, Shield, DollarSign, CheckCircle, Heart, Activity } from 'lucide-svelte';
+	import { Chart, Svg, Axis, Line, Spline, Highlight } from 'layerchart';
+	import { format } from 'date-fns';
+	import { ptBR } from 'date-fns/locale';
+	import { mockEvolucaoSentimentosData, mockMonitoramentoPlaybooksData } from '$lib/core/models/mock-data.js';
+
+	// Mock Data for KPIs
+	const mockEvolucaoKpis = {
+		churnEvitado: '--',
+		roiUpsell: '--',
+		scoreMedio: '--',
+		taxaEvolucao: '--',
+		conversaoEmocao: '--'
+	};
+
+	function formatSentimentAxis(val) {
+		if (val === 1) return 'Pos.';
+		if (val === 0) return 'Neutro';
+		if (val === -1) return 'Neg.';
+		return val.toFixed(1);
+	}
+</script>
+
+<svelte:head>
+	<title>Evolução - Nettown Analytics</title>
+</svelte:head>
+
+<div class="mx-auto max-w-[1600px] space-y-6">
+	<!-- Top Section: Header, Filters, KPIs -->
+	<div class="rounded-xl border border-slate-200 bg-white p-5 md:p-8 shadow-sm transition-colors duration-200 dark:border-slate-800 dark:bg-[#161f30]">
+		<div class="flex items-center gap-3 mb-8">
+			<div class="h-8 w-8 shrink-0 rounded-lg bg-blue-500/20 text-blue-500 flex items-center justify-center">
+				<TrendingUp size={20} strokeWidth={2.5} />
+			</div>
+			<h1 class="text-xl font-bold text-slate-900 dark:text-white">Evolução: Observatório de Resultados</h1>
+		</div>
+
+		<!-- Filters -->
+		<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 w-full text-sm mb-12">
+			<div class="flex flex-col gap-1.5">
+				<span class="text-xs font-semibold text-slate-500 dark:text-slate-400">Período</span>
+				<select class="rounded-md border border-slate-200 bg-white px-3 py-1.5 text-slate-900 focus:border-indigo-500 focus:outline-none dark:border-slate-700 dark:bg-[#0f172a] dark:text-slate-200">
+					<option>Últimos 7 dias</option>
+				</select>
+			</div>
+			<div class="flex flex-col gap-1.5">
+				<span class="text-xs font-semibold text-slate-500 dark:text-slate-400">Unidade</span>
+				<select class="rounded-md border border-slate-200 bg-white px-3 py-1.5 text-slate-900 focus:border-indigo-500 focus:outline-none dark:border-slate-700 dark:bg-[#0f172a] dark:text-slate-200">
+					<option>Sem segmento</option>
+				</select>
+			</div>
+			<div class="flex flex-col gap-1.5">
+				<span class="text-xs font-semibold text-slate-500 dark:text-slate-400">Área</span>
+				<select class="rounded-md border border-slate-200 bg-white px-3 py-1.5 text-slate-900 focus:border-indigo-500 focus:outline-none dark:border-slate-700 dark:bg-[#0f172a] dark:text-slate-200">
+					<option>Sem segmento de setor</option>
+				</select>
+			</div>
+			<div class="flex flex-col gap-1.5">
+				<span class="text-xs font-semibold text-slate-500 dark:text-slate-400">Linha</span>
+				<select class="rounded-md border border-slate-200 bg-white px-3 py-1.5 text-slate-900 focus:border-indigo-500 focus:outline-none dark:border-slate-700 dark:bg-[#0f172a] dark:text-slate-200">
+					<option>Todas</option>
+				</select>
+			</div>
+			<div class="flex flex-col gap-1.5">
+				<span class="text-xs font-semibold text-slate-500 dark:text-slate-400">Sentimento</span>
+				<select class="rounded-md border border-slate-200 bg-white px-3 py-1.5 text-slate-900 focus:border-indigo-500 focus:outline-none dark:border-slate-700 dark:bg-[#0f172a] dark:text-slate-200">
+					<option>Todos</option>
+				</select>
+			</div>
+			<div class="flex flex-col gap-1.5">
+				<span class="text-xs font-semibold text-slate-500 dark:text-slate-400">Fonte</span>
+				<select class="rounded-md border border-slate-200 bg-white px-3 py-1.5 text-slate-900 focus:border-indigo-500 focus:outline-none dark:border-slate-700 dark:bg-[#0f172a] dark:text-slate-200">
+					<option>Todas</option>
+				</select>
+			</div>
+		</div>
+
+		<!-- KPIs -->
+		<div class="grid grid-cols-2 md:grid-cols-5 gap-6 w-full pb-4">
+			<div class="flex flex-col items-center justify-center text-center">
+				<Shield size={24} class="text-red-500 mb-3 mx-auto" />
+				<div class="text-3xl font-bold text-slate-900 dark:text-white mb-1">{mockEvolucaoKpis.churnEvitado}</div>
+				<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400">Churn Evitado (Valor)</div>
+			</div>
+			<div class="flex flex-col items-center justify-center text-center">
+				<DollarSign size={24} class="text-emerald-500 mb-3 mx-auto" />
+				<div class="text-3xl font-bold text-slate-900 dark:text-white mb-1">{mockEvolucaoKpis.roiUpsell}</div>
+				<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400">ROI de Upsell</div>
+			</div>
+			<div class="flex flex-col items-center justify-center text-center">
+				<CheckCircle size={24} class="text-amber-500 mb-3 mx-auto" />
+				<div class="text-3xl font-bold text-slate-900 dark:text-white mb-1">{mockEvolucaoKpis.scoreMedio}</div>
+				<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400">Score Médio Geral</div>
+			</div>
+			<div class="flex flex-col items-center justify-center text-center">
+				<TrendingUp size={24} class="text-blue-400 mb-3 mx-auto" />
+				<div class="text-3xl font-bold text-slate-900 dark:text-white mb-1">{mockEvolucaoKpis.taxaEvolucao}</div>
+				<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400">Taxa de Evolução</div>
+			</div>
+			<div class="flex flex-col items-center justify-center text-center">
+				<Heart size={24} class="text-pink-500 mb-3 mx-auto" />
+				<div class="text-3xl font-bold text-slate-900 dark:text-white mb-1">{mockEvolucaoKpis.conversaoEmocao}</div>
+				<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400">Conversão de Emoção</div>
+			</div>
+		</div>
+	</div>
+
+	<!-- Charts Area -->
+	<div class="grid grid-cols-1 gap-6">
+		<!-- Chart 1: Evolucao dos Sentimentos Geral -->
+		<div class="rounded-xl border border-slate-200 bg-white p-5 md:p-6 shadow-sm transition-colors duration-200 dark:border-slate-800 dark:bg-[#161f30] h-[360px] flex flex-col">
+			<h2 class="text-base font-bold text-slate-900 dark:text-white mb-4">Evolução dos Sentimentos Geral</h2>
+			<div class="h-full w-full flex-1">
+				<Chart
+					data={mockEvolucaoSentimentosData}
+					x={d => d.date}
+					y={d => d.value}
+					yDomain={[-1, 1]}
+					padding={{ top: 10, right: 10, bottom: 20, left: 40 }}
+				>
+					<Svg>
+						<Axis
+							placement="left"
+							grid={{ class: 'stroke-slate-200 dark:stroke-slate-700', strokeDasharray: '2 2' }}
+							class="fill-slate-500 text-xs dark:fill-slate-400"
+							format={formatSentimentAxis}
+							ticks={[-1, -0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, 1]}
+						/>
+						<Axis
+							placement="bottom"
+							format={(d) => format(d, 'dd/MM')}
+							class="fill-slate-500 text-xs dark:fill-slate-400"
+						/>
+						<Spline stroke="#6366f1" strokeWidth={3} />
+						<Highlight
+							points={{
+								fill: '#6366f1',
+								class: 'stroke-white dark:stroke-slate-900',
+								strokeWidth: 2
+							}}
+						/>
+					</Svg>
+				</Chart>
+			</div>
+		</div>
+
+		<!-- Chart 2: Monitoramento de Playbooks -->
+		<div class="rounded-xl border border-slate-200 bg-white p-5 md:p-6 shadow-sm transition-colors duration-200 dark:border-slate-800 dark:bg-[#161f30] h-[360px] flex flex-col">
+			<div class="flex items-center justify-between mb-4">
+				<div class="flex items-center gap-4">
+					<h2 class="text-base font-bold text-slate-900 dark:text-white">Monitoramento de Playbooks (Prospecção Anônima)</h2>
+					<div class="flex items-center text-sm font-medium">
+						<button class="px-3 py-1 text-slate-400 hover:text-white transition-colors">Gráfico</button>
+						<button class="px-3 py-1 text-slate-400 hover:text-white transition-colors">Lista</button>
+					</div>
+				</div>
+				<div class="flex items-center gap-6">
+					<div class="flex flex-col items-end">
+						<span class="text-[10px] font-bold text-slate-500 uppercase tracking-wider">Total do Período</span>
+						<div class="flex gap-1 text-sm font-bold">
+							<span class="text-indigo-400">148</span>
+							<span class="text-slate-500">/</span>
+							<span class="text-emerald-400">5</span>
+						</div>
+					</div>
+					<div class="flex items-center gap-4 text-xs font-medium text-slate-400">
+						<div class="flex items-center gap-1.5">
+							<div class="w-3 h-3 rounded-sm bg-indigo-500"></div>
+							Novos Detectados
+						</div>
+						<div class="flex items-center gap-1.5">
+							<div class="w-3 h-3 rounded-sm bg-emerald-500"></div>
+							Cadastrados Convertidos
+						</div>
+					</div>
+				</div>
+			</div>
+			
+			<div class="h-full w-full flex-1">
+				<Chart
+					data={mockMonitoramentoPlaybooksData}
+					x={d => d.date}
+					yDomain={[0, 60]}
+					padding={{ top: 10, right: 10, bottom: 20, left: 30 }}
+				>
+					<Svg>
+						<Axis
+							placement="left"
+							grid={{ class: 'stroke-slate-200 dark:stroke-slate-700', strokeDasharray: '2 2' }}
+							class="fill-slate-500 text-xs dark:fill-slate-400"
+							ticks={[0, 10, 20, 30, 40, 50, 60]}
+						/>
+						<Axis
+							placement="bottom"
+							format={(d) => format(d, 'dd/MM')}
+							class="fill-slate-500 text-xs dark:fill-slate-400"
+						/>
+						
+						<Spline y={d => d.novos} stroke="#6366f1" strokeWidth={3} />
+						<Spline y={d => d.convertidos} stroke="#10b981" strokeWidth={3} />
+						
+						<Highlight
+							points={{
+								fill: 'white',
+								class: 'stroke-slate-400 dark:stroke-slate-500',
+								strokeWidth: 2
+							}}
+						/>
+					</Svg>
+				</Chart>
+			</div>
+		</div>
+	</div>
+</div>

+ 200 - 0
src/routes/(app)/dashboard/personas/+page.svelte

@@ -0,0 +1,200 @@
+<script>
+	import { Users, AlertTriangle, DollarSign, Compass, UserRound, TrendingUp, HeartHandshake } from 'lucide-svelte';
+	import { mockPersonasKpis, mockPersonas } from '$lib/core/models/mock-data.js';
+
+	let selectedPersona = $state(null);
+
+	function openPersonaDetails(persona) {
+		selectedPersona = persona;
+	}
+
+	function closePersonaDetails() {
+		selectedPersona = null;
+	}
+</script>
+
+<svelte:head>
+	<title>Laboratório de Personas - Nettown Analytics</title>
+</svelte:head>
+
+<div class="mx-auto max-w-[1600px] space-y-6">
+	<!-- Top Section: Header, Filters, KPIs -->
+	<div class="rounded-xl border border-slate-200 bg-white p-5 md:p-8 shadow-sm transition-colors duration-200 dark:border-slate-800 dark:bg-[#161f30]">
+		<div class="flex items-center gap-3 mb-8">
+			<div class="h-8 w-8 shrink-0 rounded-lg bg-amber-500/20 text-amber-500 flex items-center justify-center">
+				<Compass size={20} strokeWidth={2.5} />
+			</div>
+			<h1 class="text-xl font-bold text-slate-900 dark:text-white">Expansão: Laboratório de Personas</h1>
+		</div>
+
+		<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 w-full text-sm mb-12">
+			<div class="flex items-center justify-center gap-2">
+				<span class="text-slate-500 dark:text-slate-400 whitespace-nowrap">Período:</span>
+				<select class="rounded-md border border-slate-200 bg-white px-3 py-1.5 text-slate-900 focus:border-indigo-500 focus:outline-none dark:border-slate-700 dark:bg-[#0f172a] dark:text-slate-200">
+					<option>Últimos 7 dias</option>
+				</select>
+			</div>
+			<div class="flex items-center justify-center gap-2">
+				<span class="text-slate-500 dark:text-slate-400 whitespace-nowrap">Unidade:</span>
+				<select class="rounded-md border border-slate-200 bg-white px-3 py-1.5 text-slate-900 focus:border-indigo-500 focus:outline-none dark:border-slate-700 dark:bg-[#0f172a] dark:text-slate-200">
+					<option>Sem segmento</option>
+				</select>
+			</div>
+			<div class="flex items-center justify-center gap-2">
+				<span class="text-slate-500 dark:text-slate-400 whitespace-nowrap">Área:</span>
+				<select class="rounded-md border border-slate-200 bg-white px-3 py-1.5 text-slate-900 focus:border-indigo-500 focus:outline-none dark:border-slate-700 dark:bg-[#0f172a] dark:text-slate-200">
+					<option>Sem segmento de setor</option>
+				</select>
+			</div>
+			<div class="flex items-center justify-center gap-2">
+				<span class="text-slate-500 dark:text-slate-400 whitespace-nowrap">Sentimento:</span>
+				<select class="rounded-md border border-slate-200 bg-white px-3 py-1.5 text-slate-900 focus:border-indigo-500 focus:outline-none dark:border-slate-700 dark:bg-[#0f172a] dark:text-slate-200">
+					<option>Todos</option>
+				</select>
+			</div>
+		</div>
+
+		<!-- KPIs -->
+		<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 w-full pb-4">
+			<div class="flex flex-col items-center justify-center text-center">
+				<UserRound size={24} class="text-amber-500 mb-3 mx-auto" />
+				<div class="text-3xl font-bold text-slate-900 dark:text-white mb-1">{mockPersonasKpis.ativas}</div>
+				<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400">Personas Ativas</div>
+			</div>
+			<div class="flex flex-col items-center justify-center text-center">
+				<AlertTriangle size={24} class="text-red-500 mb-3 mx-auto" />
+				<div class="text-3xl font-bold text-slate-900 dark:text-white mb-1">{mockPersonasKpis.riscoChurn}</div>
+				<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400">Risco de Churn</div>
+			</div>
+			<div class="flex flex-col items-center justify-center text-center">
+				<DollarSign size={24} class="text-red-500 mb-3 mx-auto" />
+				<div class="text-3xl font-bold text-slate-900 dark:text-white mb-1">{mockPersonasKpis.perdaMensalEst}</div>
+				<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400">Perda Mensal Est.</div>
+			</div>
+			<div class="flex flex-col items-center justify-center text-center">
+				<DollarSign size={24} class="text-amber-500 mb-3 mx-auto" />
+				<div class="text-3xl font-bold text-slate-900 dark:text-white mb-1">{mockPersonasKpis.potencialExpansao}</div>
+				<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400">Potencial de Expansão</div>
+			</div>
+		</div>
+	</div>
+
+	<!-- Personas Grid & Details -->
+	<div class="grid grid-cols-1 xl:grid-cols-3 gap-6">
+		<div class="xl:col-span-2 rounded-xl border border-slate-200 bg-white p-5 md:p-6 shadow-sm transition-colors duration-200 dark:border-slate-800 dark:bg-[#161f30]">
+			
+			<div class="flex flex-wrap items-center justify-between gap-4 mb-8">
+				<div class="flex items-center gap-3">
+					<div class="flex items-center justify-center text-slate-700 dark:text-slate-300">
+						<UserRound size={20} />
+					</div>
+					<h2 class="text-lg font-bold text-slate-900 dark:text-white">Minhas Personas</h2>
+					<span class="text-sm text-slate-500 dark:text-slate-400">Conheça sua base de forma humana</span>
+				</div>
+				<button class="rounded-lg bg-indigo-600 px-5 py-2 text-sm font-semibold text-white transition-colors hover:bg-indigo-700 shadow-sm whitespace-nowrap">
+					Meus Clientes
+				</button>
+			</div>
+
+			<!-- Header Stats -->
+			<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8 pb-8 border-b border-slate-100 dark:border-slate-800/60">
+				<div class="flex flex-col items-start text-left">
+					<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400 mb-2">
+						Personas Identificadas
+					</div>
+					<div class="text-3xl font-bold text-slate-900 dark:text-white">0</div>
+				</div>
+				<div class="flex flex-col items-start text-left">
+					<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400 mb-2">
+						Volume Total de Mensagens Analisadas
+					</div>
+					<div class="text-3xl font-bold text-slate-900 dark:text-white">0</div>
+				</div>
+				<div class="flex flex-col items-start text-left">
+					<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400 mb-2">
+						Aspectos Únicos
+					</div>
+					<div class="text-3xl font-bold text-slate-900 dark:text-white">0</div>
+				</div>
+				<div class="flex flex-col items-start text-left">
+					<div class="text-[11px] font-semibold tracking-wider text-slate-500 uppercase dark:text-slate-400 mb-2">
+						Subaspectos Únicos
+					</div>
+					<div class="text-3xl font-bold text-slate-900 dark:text-white">0</div>
+				</div>
+			</div>
+
+			<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
+				{#each mockPersonas as persona}
+					<button
+						type="button"
+						onclick={() => openPersonaDetails(persona)}
+						class={`flex flex-col items-center justify-start rounded-xl border p-6 text-center transition-all duration-200 ${
+							selectedPersona?.id === persona.id
+								? 'border-indigo-400 bg-indigo-50/50 shadow-[0_0_15px_rgba(99,102,241,0.15)] dark:border-indigo-500/80 dark:bg-[#1e293b] dark:shadow-[0_0_20px_rgba(99,102,241,0.1)]'
+								: 'border-slate-200 bg-white hover:border-slate-300 dark:border-slate-700/60 dark:bg-[#1e293b]/40 dark:hover:border-slate-600 dark:hover:bg-[#1e293b]/60'
+						}`}
+					>
+						<span class="inline-block rounded-full bg-indigo-100 px-4 py-1 text-[10px] font-bold text-indigo-700 dark:bg-indigo-500/20 dark:text-indigo-400 mb-6 tracking-wider">
+							{persona.tipo}
+						</span>
+						<UserRound size={36} strokeWidth={1.5} class="text-slate-400 dark:text-slate-500 mb-4 mx-auto" />
+						<h3 class="text-sm font-bold text-slate-900 dark:text-white mb-2 leading-snug text-center">{persona.nome}</h3>
+						<p class="text-xs text-slate-500 dark:text-slate-400 leading-relaxed font-medium text-center">{persona.descricao}</p>
+					</button>
+				{/each}
+			</div>
+		</div>
+
+		<!-- Next Best Action Panel -->
+		<div class="rounded-xl border border-slate-200 bg-white p-5 shadow-sm transition-colors duration-200 dark:border-slate-800 dark:bg-[#1e293b] flex flex-col h-[600px]">
+			<div class="mb-4">
+				<h2 class="text-lg font-bold text-slate-900 dark:text-white">Next Best Action</h2>
+				<p class="text-sm text-slate-500 dark:text-slate-400">Recomendações estratégicas por persona</p>
+			</div>
+			
+			<div class="flex-1 overflow-y-auto custom-scrollbar pr-2">
+				{#if selectedPersona}
+					<div class="space-y-6">
+						<div>
+							<div class="flex items-center gap-2 mb-2">
+								<UserRound size={18} class="text-indigo-500" />
+								<h3 class="font-bold text-slate-900 dark:text-white">{selectedPersona.nome}</h3>
+							</div>
+							<p class="text-sm text-slate-600 dark:text-slate-300 bg-slate-50 dark:bg-slate-800 p-3 rounded-lg border border-slate-100 dark:border-slate-700">
+								{selectedPersona.detalhes}
+							</p>
+						</div>
+
+						<div class="space-y-4">
+							<div class="rounded-lg border border-emerald-200 bg-emerald-50 p-4 dark:border-emerald-500/20 dark:bg-emerald-500/5">
+								<h4 class="font-bold text-emerald-800 dark:text-emerald-400 mb-2 flex items-center gap-2">
+									<TrendingUp size={16} />
+									Expansão
+								</h4>
+								<p class="text-sm text-emerald-700 dark:text-emerald-300">
+									{selectedPersona.expansao}
+								</p>
+							</div>
+
+							<div class="rounded-lg border border-red-200 bg-red-50 p-4 dark:border-red-500/20 dark:bg-red-500/5">
+								<h4 class="font-bold text-red-800 dark:text-red-400 mb-2 flex items-center gap-2">
+									<HeartHandshake size={16} />
+									Engajamento (Salvar Churn)
+								</h4>
+								<p class="text-sm text-red-700 dark:text-red-300">
+									{selectedPersona.engajamento}
+								</p>
+							</div>
+						</div>
+					</div>
+				{:else}
+					<div class="flex h-full flex-col items-center justify-center text-center text-slate-500 dark:text-slate-400">
+						<UserRound size={48} class="mb-4 opacity-20" />
+						<p>Selecione uma persona ao lado para ver o Next Best Action.</p>
+					</div>
+				{/if}
+			</div>
+		</div>
+	</div>
+</div>

+ 206 - 0
src/routes/(app)/dashboard/settings/+page.svelte

@@ -0,0 +1,206 @@
+<script>
+	import { Settings, Phone, Users, Bell, Shield, Database, Globe, Palette, HelpCircle } from 'lucide-svelte';
+	import { goto } from '$app/navigation';
+
+	// Settings sections
+	const settingsSections = [
+		{
+			title: 'Integrações',
+			icon: Phone,
+			items: [
+				{
+					title: 'WhatsApp Business',
+					description: 'Configure sua conta do WhatsApp Business para comunicação',
+					status: 'connected',
+					action: 'gerenciar'
+				},
+				{
+					title: 'WhatsApp dos Vendedores',
+					description: 'Adicione os números de WhatsApp da equipe de vendas',
+					status: 'partial',
+					action: 'configurar',
+					count: '3 de 5 configurados'
+				}
+			]
+		},
+		{
+			title: 'Notificações',
+			icon: Bell,
+			items: [
+				{
+					title: 'Alertas de Sentimento',
+					description: 'Receba alertas sobre mudanças no sentimento dos clientes',
+					status: 'enabled',
+					action: 'configurar'
+				},
+				{
+					title: 'Relatórios Diários',
+					description: 'Receba relatórios automáticos por e-mail',
+					status: 'disabled',
+					action: 'ativar'
+				}
+			]
+		},
+		{
+			title: 'Segurança',
+			icon: Shield,
+			items: [
+				{
+					title: 'Autenticação de Dois Fatores',
+					description: 'Proteja sua conta com autenticação adicional',
+					status: 'disabled',
+					action: 'ativar'
+				},
+				{
+					title: 'Permissões de Acesso',
+					description: 'Gerencie quem pode acessar o painel',
+					status: 'configured',
+					action: 'gerenciar'
+				}
+			]
+		},
+		{
+			title: 'Dados',
+			icon: Database,
+			items: [
+				{
+					title: 'Exportar Dados',
+					description: 'Exporte seus dados em formato CSV ou Excel',
+					status: 'available',
+					action: 'exportar'
+				},
+				{
+					title: 'Backup Automático',
+					description: 'Configure backups automáticos dos seus dados',
+					status: 'enabled',
+					action: 'gerenciar'
+				}
+			]
+		},
+		{
+			title: 'Personalização',
+			icon: Palette,
+			items: [
+				{
+					title: 'Tema',
+					description: 'Escolha entre modo claro ou escuro',
+					status: 'dark',
+					action: 'mudar'
+				},
+				{
+					title: 'Idioma',
+					description: 'Selecione o idioma da interface',
+					status: 'pt-BR',
+					action: 'mudar'
+				}
+			]
+		},
+		{
+			title: 'Suporte',
+			icon: HelpCircle,
+			items: [
+				{
+					title: 'Central de Ajuda',
+					description: 'Acesse nossa documentação e tutoriais',
+					status: 'available',
+					action: 'acessar'
+				},
+				{
+					title: 'Contato',
+					description: 'Fale com nossa equipe de suporte',
+					status: 'available',
+					action: 'abrir chamado'
+				}
+			]
+		}
+	];
+
+	function handleAction(item) {
+		// Handle different actions based on item
+		console.log(`Action: ${item.action} for ${item.title}`);
+		
+		// For WhatsApp integration, we could open a modal or navigate to a specific page
+		if (item.title === 'WhatsApp Business') {
+			// TODO: Open WhatsApp configuration modal
+		} else if (item.title === 'WhatsApp dos Vendedores') {
+			// TODO: Open sellers WhatsApp configuration
+		}
+	}
+
+	function getStatusBadge(status) {
+		const statusConfig = {
+			connected: { text: 'Conectado', class: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' },
+			partial: { text: 'Parcial', class: 'bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400' },
+			enabled: { text: 'Ativo', class: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' },
+			disabled: { text: 'Inativo', class: 'bg-slate-100 text-slate-800 dark:bg-slate-900/30 dark:text-slate-400' },
+			configured: { text: 'Configurado', class: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' },
+			available: { text: 'Disponível', class: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400' },
+			dark: { text: 'Escuro', class: 'bg-slate-100 text-slate-800 dark:bg-slate-900/30 dark:text-slate-400' },
+			'pt-BR': { text: 'Português', class: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400' }
+		};
+
+		return statusConfig[status] || statusConfig.available;
+	}
+</script>
+
+<div class="mx-auto max-w-[1600px] space-y-6">
+	<!-- Header -->
+	<div class="rounded-xl border border-slate-200 bg-white p-5 md:p-6 shadow-sm transition-colors duration-200 dark:border-slate-800 dark:bg-[#161f30]">
+		<div class="flex items-center gap-3">
+			<div class="h-8 w-8 shrink-0 rounded-lg bg-indigo-500/20 text-indigo-500 flex items-center justify-center">
+				<Settings size={20} strokeWidth={2.5} />
+			</div>
+			<h1 class="text-xl font-bold text-slate-900 dark:text-white">Configurações</h1>
+		</div>
+		<p class="mt-2 text-sm text-slate-600 dark:text-slate-400">
+			Gerencie as configurações do sistema, integrações e preferências da sua conta
+		</p>
+	</div>
+
+	<!-- Settings Sections -->
+	<div class="grid grid-cols-1 gap-6">
+		{#each settingsSections as section}
+			<div class="rounded-xl border border-slate-200 bg-white shadow-sm transition-colors duration-200 dark:border-slate-800 dark:bg-[#161f30]">
+				<!-- Section Header -->
+				<div class="border-b border-slate-200 p-5 dark:border-slate-800">
+					<div class="flex items-center gap-3">
+						<div class="h-8 w-8 shrink-0 rounded-lg bg-slate-100 text-slate-600 flex items-center justify-center dark:bg-slate-800 dark:text-slate-400">
+							<section.icon size={18} strokeWidth={2} />
+						</div>
+						<h2 class="text-lg font-semibold text-slate-900 dark:text-white">{section.title}</h2>
+					</div>
+				</div>
+
+				<!-- Section Items -->
+				<div class="divide-y divide-slate-200 dark:divide-slate-800">
+					{#each section.items as item}
+						<div class="p-5 hover:bg-slate-50 transition-colors duration-150 dark:hover:bg-slate-800/50">
+							<div class="flex items-center justify-between">
+								<div class="flex-1 min-w-0">
+									<div class="flex items-center gap-3">
+										<h3 class="text-sm font-medium text-slate-900 dark:text-white">{item.title}</h3>
+										<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium {getStatusBadge(item.status).class}">
+											{getStatusBadge(item.status).text}
+										</span>
+									</div>
+									<p class="mt-1 text-sm text-slate-600 dark:text-slate-400">{item.description}</p>
+									{#if item.count}
+										<p class="mt-1 text-xs text-amber-600 dark:text-amber-400">{item.count}</p>
+									{/if}
+								</div>
+								<div class="ml-4">
+									<button
+										onclick={() => handleAction(item)}
+										class="inline-flex items-center px-3 py-1.5 text-xs font-medium rounded-md bg-indigo-600 text-white hover:bg-indigo-700 transition-colors duration-150"
+									>
+										{item.action}
+									</button>
+								</div>
+							</div>
+						</div>
+					{/each}
+				</div>
+			</div>
+		{/each}
+	</div>
+</div>