Files
sgse-app/apps/web/src/lib/components/chat/ChatWidget.svelte

191 lines
5.4 KiB
Svelte

<script lang="ts">
import {
chatAberto,
chatMinimizado,
conversaAtiva,
fecharChat,
minimizarChat,
maximizarChat,
abrirChat,
} from "$lib/stores/chatStore";
import { useQuery } from "convex-svelte";
import { api } from "@sgse-app/backend/convex/_generated/api";
import ChatList from "./ChatList.svelte";
import ChatWindow from "./ChatWindow.svelte";
const count = useQuery(api.chat.contarNotificacoesNaoLidas, {});
let isOpen = $state(false);
let isMinimized = $state(false);
let activeConversation = $state<string | null>(null);
// Sincronizar com stores
$effect(() => {
isOpen = $chatAberto;
});
$effect(() => {
isMinimized = $chatMinimizado;
});
$effect(() => {
activeConversation = $conversaAtiva;
});
function handleToggle() {
if (isOpen && !isMinimized) {
minimizarChat();
} else {
abrirChat();
}
}
function handleClose() {
fecharChat();
}
function handleMinimize() {
minimizarChat();
}
function handleMaximize() {
maximizarChat();
}
</script>
<!-- Botão flutuante (quando fechado ou minimizado) -->
{#if !isOpen || isMinimized}
<button
type="button"
class="fixed bottom-6 right-6 btn btn-circle btn-primary btn-lg shadow-2xl z-50 hover:scale-110 transition-transform"
onclick={handleToggle}
aria-label="Abrir chat"
>
<!-- Ícone de chat -->
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-7 h-7"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M8.625 12a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 0 1-2.555-.337A5.972 5.972 0 0 1 5.41 20.97a5.969 5.969 0 0 1-.474-.065 4.48 4.48 0 0 0 .978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25Z"
/>
</svg>
<!-- Badge de contador -->
{#if count && count > 0}
<span
class="absolute -top-1 -right-1 flex h-6 w-6 items-center justify-center rounded-full bg-error text-error-content text-xs font-bold"
>
{count > 9 ? "9+" : count}
</span>
{/if}
</button>
{/if}
<!-- Janela do Chat -->
{#if isOpen && !isMinimized}
<div
class="fixed bottom-6 right-6 z-50 flex flex-col bg-base-100 rounded-2xl shadow-2xl border border-base-300 overflow-hidden
w-[400px] h-[600px] max-w-[calc(100vw-3rem)] max-h-[calc(100vh-3rem)]
md:w-[400px] md:h-[600px]
sm:w-full sm:h-full sm:bottom-0 sm:right-0 sm:rounded-none sm:max-w-full sm:max-h-full"
style="animation: slideIn 0.3s ease-out;"
>
<!-- Header -->
<div
class="flex items-center justify-between px-4 py-3 bg-primary text-primary-content border-b border-primary-focus"
>
<h2 class="text-lg font-semibold flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M8.625 12a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 0 1-2.555-.337A5.972 5.972 0 0 1 5.41 20.97a5.969 5.969 0 0 1-.474-.065 4.48 4.48 0 0 0 .978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25Z"
/>
</svg>
Chat
</h2>
<div class="flex items-center gap-1">
<!-- Botão minimizar -->
<button
type="button"
class="btn btn-ghost btn-sm btn-circle"
onclick={handleMinimize}
aria-label="Minimizar"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-5 h-5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14" />
</svg>
</button>
<!-- Botão fechar -->
<button
type="button"
class="btn btn-ghost btn-sm btn-circle"
onclick={handleClose}
aria-label="Fechar"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-5 h-5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M6 18 18 6M6 6l12 12"
/>
</svg>
</button>
</div>
</div>
<!-- Conteúdo -->
<div class="flex-1 overflow-hidden">
{#if !activeConversation}
<ChatList />
{:else}
<ChatWindow conversaId={activeConversation} />
{/if}
</div>
</div>
{/if}
<style>
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(20px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
</style>