diff --git a/apps/web/src/lib/components/chat/ChatList.svelte b/apps/web/src/lib/components/chat/ChatList.svelte index 23fea2b..625d8ab 100644 --- a/apps/web/src/lib/components/chat/ChatList.svelte +++ b/apps/web/src/lib/components/chat/ChatList.svelte @@ -9,6 +9,7 @@ import NewConversationModal from './NewConversationModal.svelte'; import { Search, Plus, MessageSquare, Users, UsersRound } from 'lucide-svelte'; import type { Id, Doc } from '@sgse-app/backend/convex/_generated/dataModel'; + import { obterCoresDoTema } from '$lib/utils/temas'; const client = useConvexClient(); @@ -23,6 +24,57 @@ let searchQuery = $state(''); let activeTab = $state<'usuarios' | 'conversas'>('usuarios'); + + // Obter cores do tema atual (reativo) + let coresTema = $state(obterCoresDoTema()); + + // Atualizar cores quando o tema mudar + $effect(() => { + if (typeof window === 'undefined') return; + + const atualizarCores = () => { + coresTema = obterCoresDoTema(); + }; + + atualizarCores(); + + window.addEventListener('themechange', atualizarCores); + + const observer = new MutationObserver(atualizarCores); + const htmlElement = document.documentElement; + observer.observe(htmlElement, { + attributes: true, + attributeFilter: ['data-theme'] + }); + + return () => { + window.removeEventListener('themechange', atualizarCores); + observer.disconnect(); + }; + }); + + // Função para obter rgba da cor primária + function obterPrimariaRgba(alpha: number = 1) { + const primary = coresTema.primary; + if (primary.startsWith('rgba')) { + const match = primary.match(/rgba?\(([^)]+)\)/); + if (match) { + const values = match[1].split(','); + return `rgba(${values[0]}, ${values[1]}, ${values[2]}, ${alpha})`; + } + } + if (primary.startsWith('#')) { + const hex = primary.replace('#', ''); + const r = parseInt(hex.substring(0, 2), 16); + const g = parseInt(hex.substring(2, 4), 16); + const b = parseInt(hex.substring(4, 6), 16); + return `rgba(${r}, ${g}, ${b}, ${alpha})`; + } + if (primary.startsWith('hsl')) { + return primary.replace(/\)$/, `, ${alpha})`).replace('hsl', 'hsla'); + } + return `rgba(102, 126, 234, ${alpha})`; + } // Debug: monitorar carregamento de dados $effect(() => { @@ -263,7 +315,7 @@
diff --git a/apps/web/src/lib/components/chat/ChatWidget.svelte b/apps/web/src/lib/components/chat/ChatWidget.svelte index 64a893e..970b0ba 100644 --- a/apps/web/src/lib/components/chat/ChatWidget.svelte +++ b/apps/web/src/lib/components/chat/ChatWidget.svelte @@ -17,6 +17,7 @@ import ChatList from './ChatList.svelte'; import ChatWindow from './ChatWindow.svelte'; import { MessageSquare, Minus, Maximize2, X, Bell } from 'lucide-svelte'; + import { obterCoresDoTema, obterTemaPersistidoNoLocalStorage } from '$lib/utils/temas'; const count = useQuery(api.chat.contarNotificacoesNaoLidas, {}); @@ -955,6 +956,80 @@ window.removeEventListener('touchend', handleTouchEnd); }; }); + + // Obter cores do tema atual (reativo) + let coresTema = $state(obterCoresDoTema()); + + // Atualizar cores quando o tema mudar + $effect(() => { + if (typeof window === 'undefined') return; + + const atualizarCores = () => { + coresTema = obterCoresDoTema(); + }; + + // Atualizar cores inicialmente + atualizarCores(); + + // Escutar mudanças de tema + window.addEventListener('themechange', atualizarCores); + + // Observar mudanças no atributo data-theme do HTML + const observer = new MutationObserver(atualizarCores); + const htmlElement = document.documentElement; + observer.observe(htmlElement, { + attributes: true, + attributeFilter: ['data-theme'] + }); + + return () => { + window.removeEventListener('themechange', atualizarCores); + observer.disconnect(); + }; + }); + + // Função para obter gradiente do tema + function obterGradienteTema() { + const primary = coresTema.primary; + // Criar variações da cor primária para o gradiente + return `linear-gradient(135deg, ${primary} 0%, ${primary}dd 50%, ${primary}bb 100%)`; + } + + // Função para obter rgba da cor primária + function obterPrimariaRgba(alpha: number = 1) { + const primary = coresTema.primary.trim(); + // Se já for rgba, extrair os valores + if (primary.startsWith('rgba')) { + const match = primary.match(/rgba?\(([^)]+)\)/); + if (match) { + const values = match[1].split(',').map(v => v.trim()); + if (values.length >= 3) { + return `rgba(${values[0]}, ${values[1]}, ${values[2]}, ${alpha})`; + } + } + } + // Se for hex, converter + if (primary.startsWith('#')) { + const hex = primary.replace('#', ''); + if (hex.length === 6) { + const r = parseInt(hex.substring(0, 2), 16); + const g = parseInt(hex.substring(2, 4), 16); + const b = parseInt(hex.substring(4, 6), 16); + return `rgba(${r}, ${g}, ${b}, ${alpha})`; + } + } + // Se for hsl, converter para hsla + if (primary.startsWith('hsl')) { + const match = primary.match(/hsl\(([^)]+)\)/); + if (match) { + return `hsla(${match[1]}, ${alpha})`; + } + // Fallback: tentar adicionar alpha + return primary.replace(/\)$/, `, ${alpha})`).replace('hsl', 'hsla'); + } + // Fallback padrão + return `rgba(102, 126, 234, ${alpha})`; + } @@ -975,10 +1050,10 @@ bottom: {bottomPos}; right: {rightPos}; position: fixed !important; - background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%); + background: {obterGradienteTema()}; box-shadow: - 0 20px 60px -10px rgba(102, 126, 234, 0.5), - 0 10px 30px -5px rgba(118, 75, 162, 0.4), + 0 20px 60px -10px {obterPrimariaRgba(0.5)}, + 0 10px 30px -5px {obterPrimariaRgba(0.4)}, 0 0 0 1px rgba(255, 255, 255, 0.1) inset; border-radius: 50%; cursor: {isDragging ? 'grabbing' : 'grab'}; @@ -1058,17 +1133,17 @@ strokeWidth={2} /> - + {#if count?.data && count.data > 0} @@ -1121,8 +1196,8 @@
handleResizeStart(e, 'n')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'n')} style="border-radius: 24px 24px 0 0;" @@ -1249,7 +1325,8 @@ role="button" tabindex="0" aria-label="Redimensionar janela pela borda inferior" - class="hover:bg-primary/20 absolute right-0 bottom-0 left-0 z-50 h-2 cursor-ns-resize transition-colors" + class="absolute right-0 bottom-0 left-0 z-50 h-2 cursor-ns-resize transition-colors" + style="--hover-bg: {obterPrimariaRgba(0.2)}" onmousedown={(e) => handleResizeStart(e, 's')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 's')} style="border-radius: 0 0 24px 24px;" @@ -1259,7 +1336,8 @@ role="button" tabindex="0" aria-label="Redimensionar janela pela borda esquerda" - class="hover:bg-primary/20 absolute top-0 bottom-0 left-0 z-50 w-2 cursor-ew-resize transition-colors" + class="absolute top-0 bottom-0 left-0 z-50 w-2 cursor-ew-resize transition-colors" + style="--hover-bg: {obterPrimariaRgba(0.2)}" onmousedown={(e) => handleResizeStart(e, 'w')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'w')} style="border-radius: 24px 0 0 24px;" @@ -1269,7 +1347,8 @@ role="button" tabindex="0" aria-label="Redimensionar janela pela borda direita" - class="hover:bg-primary/20 absolute top-0 right-0 bottom-0 z-50 w-2 cursor-ew-resize transition-colors" + class="absolute top-0 right-0 bottom-0 z-50 w-2 cursor-ew-resize transition-colors" + style="--hover-bg: {obterPrimariaRgba(0.2)}" onmousedown={(e) => handleResizeStart(e, 'e')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'e')} style="border-radius: 0 24px 24px 0;" @@ -1279,7 +1358,8 @@ role="button" tabindex="0" aria-label="Redimensionar janela pelo canto superior esquerdo" - class="hover:bg-primary/20 absolute top-0 left-0 z-50 h-4 w-4 cursor-nwse-resize transition-colors" + class="absolute top-0 left-0 z-50 h-4 w-4 cursor-nwse-resize transition-colors" + style="--hover-bg: {obterPrimariaRgba(0.2)}" onmousedown={(e) => handleResizeStart(e, 'nw')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'nw')} style="border-radius: 24px 0 0 0;" @@ -1288,7 +1368,8 @@ role="button" tabindex="0" aria-label="Redimensionar janela pelo canto superior direito" - class="hover:bg-primary/20 absolute top-0 right-0 z-50 h-4 w-4 cursor-nesw-resize transition-colors" + class="absolute top-0 right-0 z-50 h-4 w-4 cursor-nesw-resize transition-colors" + style="--hover-bg: {obterPrimariaRgba(0.2)}" onmousedown={(e) => handleResizeStart(e, 'ne')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'ne')} style="border-radius: 0 24px 0 0;" @@ -1297,7 +1378,8 @@ role="button" tabindex="0" aria-label="Redimensionar janela pelo canto inferior esquerdo" - class="hover:bg-primary/20 absolute bottom-0 left-0 z-50 h-4 w-4 cursor-nesw-resize transition-colors" + class="absolute bottom-0 left-0 z-50 h-4 w-4 cursor-nesw-resize transition-colors" + style="--hover-bg: {obterPrimariaRgba(0.2)}" onmousedown={(e) => handleResizeStart(e, 'sw')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'sw')} style="border-radius: 0 0 0 24px;" @@ -1306,7 +1388,8 @@ role="button" tabindex="0" aria-label="Redimensionar janela pelo canto inferior direito" - class="hover:bg-primary/20 absolute right-0 bottom-0 z-50 h-4 w-4 cursor-nwse-resize transition-colors" + class="absolute right-0 bottom-0 z-50 h-4 w-4 cursor-nwse-resize transition-colors" + style="--hover-bg: {obterPrimariaRgba(0.2)}" onmousedown={(e) => handleResizeStart(e, 'se')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'se')} style="border-radius: 0 0 24px 0;" @@ -1324,8 +1407,8 @@ role="button" tabindex="0" aria-label="Abrir conversa: Nova mensagem de {notificationMsg.remetente}" - class="bg-base-100 border-primary/20 fixed top-4 right-4 z-1000 max-w-sm cursor-pointer rounded-lg border p-4 shadow-2xl" - style="box-shadow: 0 10px 40px -10px rgba(0,0,0,0.3); animation: slideInRight 0.3s ease-out;" + class="bg-base-100 fixed top-4 right-4 z-1000 max-w-sm cursor-pointer rounded-lg border p-4 shadow-2xl" + style="border-color: {obterPrimariaRgba(0.2)}; box-shadow: 0 10px 40px -10px rgba(0,0,0,0.3); animation: slideInRight 0.3s ease-out;" onclick={() => { const conversaIdToOpen = notificationMsg?.conversaId; showGlobalNotificationPopup = false; @@ -1356,8 +1439,8 @@ }} >
-
- +
+

@@ -1366,7 +1449,7 @@

{notificationMsg.conteudo}

-

Clique para abrir

+

Clique para abrir

@@ -289,7 +351,7 @@ userId={conversa()?.outroUsuario?._id} /> {:else} -
+
{getAvatarConversa()}
{/if} @@ -380,7 +442,8 @@
{#if conversa()?.tipo === 'sala_reuniao' && isAdmin?.data} • Admin {/if} @@ -727,10 +790,8 @@ {#each searchResults as resultado, index (resultado._id)}