refactor: clean up Svelte components and improve code readability
- Refactored multiple Svelte components to enhance code clarity and maintainability. - Standardized formatting and indentation across various files for consistency. - Improved error handling messages in the AprovarAusencias component for better user feedback. - Updated class names in the UI components to align with the new design system. - Removed unnecessary whitespace and comments to streamline the codebase.
This commit is contained in:
@@ -9,10 +9,10 @@
|
||||
import NewConversationModal from "./NewConversationModal.svelte";
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
|
||||
// Buscar todos os usuários para o chat
|
||||
const usuarios = useQuery(api.usuarios.listarParaChat, {});
|
||||
|
||||
|
||||
// Buscar o perfil do usuário logado
|
||||
const meuPerfil = useQuery(api.usuarios.obterPerfil, {});
|
||||
|
||||
@@ -24,54 +24,77 @@
|
||||
|
||||
// Debug: monitorar carregamento de dados
|
||||
$effect(() => {
|
||||
console.log("📊 [ChatList] Usuários carregados:", usuarios?.data?.length || 0);
|
||||
console.log("👤 [ChatList] Meu perfil:", meuPerfil?.data?.nome || "Carregando...");
|
||||
console.log("🆔 [ChatList] Meu ID:", meuPerfil?.data?._id || "Não encontrado");
|
||||
console.log(
|
||||
"📊 [ChatList] Usuários carregados:",
|
||||
usuarios?.data?.length || 0,
|
||||
);
|
||||
console.log(
|
||||
"👤 [ChatList] Meu perfil:",
|
||||
meuPerfil?.data?.nome || "Carregando...",
|
||||
);
|
||||
console.log(
|
||||
"🆔 [ChatList] Meu ID:",
|
||||
meuPerfil?.data?._id || "Não encontrado",
|
||||
);
|
||||
if (usuarios?.data) {
|
||||
const meuId = meuPerfil?.data?._id;
|
||||
const meusDadosNaLista = usuarios.data.find((u: any) => u._id === meuId);
|
||||
if (meusDadosNaLista) {
|
||||
console.warn("⚠️ [ChatList] ATENÇÃO: Meu usuário está na lista do backend!", meusDadosNaLista.nome);
|
||||
console.warn(
|
||||
"⚠️ [ChatList] ATENÇÃO: Meu usuário está na lista do backend!",
|
||||
meusDadosNaLista.nome,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const usuariosFiltrados = $derived.by(() => {
|
||||
if (!usuarios?.data || !Array.isArray(usuarios.data)) return [];
|
||||
|
||||
|
||||
// Se não temos o perfil ainda, retornar lista vazia para evitar mostrar usuários incorretos
|
||||
if (!meuPerfil?.data) {
|
||||
console.log("⏳ [ChatList] Aguardando perfil do usuário...");
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
const meuId = meuPerfil.data._id;
|
||||
|
||||
|
||||
// Filtrar o próprio usuário da lista (filtro de segurança no frontend)
|
||||
let listaFiltrada = usuarios.data.filter((u: any) => u._id !== meuId);
|
||||
|
||||
|
||||
// Log se ainda estiver na lista após filtro (não deveria acontecer)
|
||||
const aindaNaLista = listaFiltrada.find((u: any) => u._id === meuId);
|
||||
if (aindaNaLista) {
|
||||
console.error("❌ [ChatList] ERRO: Meu usuário ainda está na lista após filtro!");
|
||||
console.error(
|
||||
"❌ [ChatList] ERRO: Meu usuário ainda está na lista após filtro!",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Aplicar busca por nome/email/matrícula
|
||||
if (searchQuery.trim()) {
|
||||
const query = searchQuery.toLowerCase();
|
||||
listaFiltrada = listaFiltrada.filter((u: any) =>
|
||||
u.nome?.toLowerCase().includes(query) ||
|
||||
u.email?.toLowerCase().includes(query) ||
|
||||
u.matricula?.toLowerCase().includes(query)
|
||||
listaFiltrada = listaFiltrada.filter(
|
||||
(u: any) =>
|
||||
u.nome?.toLowerCase().includes(query) ||
|
||||
u.email?.toLowerCase().includes(query) ||
|
||||
u.matricula?.toLowerCase().includes(query),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Ordenar: Online primeiro, depois por nome
|
||||
return listaFiltrada.sort((a: any, b: any) => {
|
||||
const statusOrder = { online: 0, ausente: 1, externo: 2, em_reuniao: 3, offline: 4 };
|
||||
const statusA = statusOrder[a.statusPresenca as keyof typeof statusOrder] ?? 4;
|
||||
const statusB = statusOrder[b.statusPresenca as keyof typeof statusOrder] ?? 4;
|
||||
|
||||
const statusOrder = {
|
||||
online: 0,
|
||||
ausente: 1,
|
||||
externo: 2,
|
||||
em_reuniao: 3,
|
||||
offline: 4,
|
||||
};
|
||||
const statusA =
|
||||
statusOrder[a.statusPresenca as keyof typeof statusOrder] ?? 4;
|
||||
const statusB =
|
||||
statusOrder[b.statusPresenca as keyof typeof statusOrder] ?? 4;
|
||||
|
||||
if (statusA !== statusB) return statusA - statusB;
|
||||
return a.nome.localeCompare(b.nome);
|
||||
});
|
||||
@@ -101,19 +124,22 @@
|
||||
try {
|
||||
processando = true;
|
||||
console.log("🔄 Clicou no usuário:", usuario.nome, "ID:", usuario._id);
|
||||
|
||||
|
||||
// Criar ou buscar conversa individual com este usuário
|
||||
console.log("📞 Chamando mutation criarOuBuscarConversaIndividual...");
|
||||
const conversaId = await client.mutation(api.chat.criarOuBuscarConversaIndividual, {
|
||||
outroUsuarioId: usuario._id,
|
||||
});
|
||||
|
||||
const conversaId = await client.mutation(
|
||||
api.chat.criarOuBuscarConversaIndividual,
|
||||
{
|
||||
outroUsuarioId: usuario._id,
|
||||
},
|
||||
);
|
||||
|
||||
console.log("✅ Conversa criada/encontrada. ID:", conversaId);
|
||||
|
||||
|
||||
// Abrir a conversa
|
||||
console.log("📂 Abrindo conversa...");
|
||||
abrirConversa(conversaId as any);
|
||||
|
||||
|
||||
console.log("✅ Conversa aberta com sucesso!");
|
||||
} catch (error) {
|
||||
console.error("❌ Erro ao abrir conversa:", error);
|
||||
@@ -122,7 +148,9 @@
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
usuario: usuario,
|
||||
});
|
||||
alert(`Erro ao abrir conversa: ${error instanceof Error ? error.message : String(error)}`);
|
||||
alert(
|
||||
`Erro ao abrir conversa: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
} finally {
|
||||
processando = false;
|
||||
}
|
||||
@@ -142,19 +170,17 @@
|
||||
// Filtrar conversas por tipo e busca
|
||||
const conversasFiltradas = $derived(() => {
|
||||
if (!conversas?.data) return [];
|
||||
|
||||
let lista = conversas.data.filter((c: any) =>
|
||||
c.tipo === "grupo" || c.tipo === "sala_reuniao"
|
||||
|
||||
let lista = conversas.data.filter(
|
||||
(c: any) => c.tipo === "grupo" || c.tipo === "sala_reuniao",
|
||||
);
|
||||
|
||||
|
||||
// Aplicar busca
|
||||
if (searchQuery.trim()) {
|
||||
const query = searchQuery.toLowerCase();
|
||||
lista = lista.filter((c: any) =>
|
||||
c.nome?.toLowerCase().includes(query)
|
||||
);
|
||||
lista = lista.filter((c: any) => c.nome?.toLowerCase().includes(query));
|
||||
}
|
||||
|
||||
|
||||
return lista;
|
||||
});
|
||||
|
||||
@@ -165,7 +191,9 @@
|
||||
abrirConversa(conversa._id);
|
||||
} catch (error) {
|
||||
console.error("Erro ao abrir conversa:", error);
|
||||
alert(`Erro ao abrir conversa: ${error instanceof Error ? error.message : String(error)}`);
|
||||
alert(
|
||||
`Erro ao abrir conversa: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
} finally {
|
||||
processando = false;
|
||||
}
|
||||
@@ -218,7 +246,7 @@
|
||||
💬 Conversas ({conversasFiltradas().length})
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Botão Nova Conversa -->
|
||||
<div class="px-4 pb-2 flex justify-end">
|
||||
<button
|
||||
@@ -236,7 +264,11 @@
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4 mr-1"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 4.5v15m7.5-7.5h-15"
|
||||
/>
|
||||
</svg>
|
||||
Nova Conversa
|
||||
</button>
|
||||
@@ -247,17 +279,21 @@
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
{#if activeTab === "usuarios"}
|
||||
<!-- Lista de usuários -->
|
||||
{#if usuarios?.data && usuariosFiltrados.length > 0}
|
||||
{#each usuariosFiltrados as usuario (usuario._id)}
|
||||
<button
|
||||
type="button"
|
||||
class="w-full text-left px-4 py-3 hover:bg-base-200 border-b border-base-300 transition-colors flex items-center gap-3 {processando ? 'opacity-50 cursor-wait' : 'cursor-pointer'}"
|
||||
onclick={() => handleClickUsuario(usuario)}
|
||||
disabled={processando}
|
||||
>
|
||||
{#if usuarios?.data && usuariosFiltrados.length > 0}
|
||||
{#each usuariosFiltrados as usuario (usuario._id)}
|
||||
<button
|
||||
type="button"
|
||||
class="w-full text-left px-4 py-3 hover:bg-base-200 border-b border-base-300 transition-colors flex items-center gap-3 {processando
|
||||
? 'opacity-50 cursor-wait'
|
||||
: 'cursor-pointer'}"
|
||||
onclick={() => handleClickUsuario(usuario)}
|
||||
disabled={processando}
|
||||
>
|
||||
<!-- Ícone de mensagem -->
|
||||
<div class="flex-shrink-0 w-10 h-10 rounded-xl flex items-center justify-center transition-all duration-300 hover:scale-110"
|
||||
style="background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); border: 1px solid rgba(102, 126, 234, 0.2);">
|
||||
<div
|
||||
class="flex-shrink-0 w-10 h-10 rounded-xl flex items-center justify-center transition-all duration-300 hover:scale-110"
|
||||
style="background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); border: 1px solid rgba(102, 126, 234, 0.2);"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -268,72 +304,83 @@
|
||||
stroke-linejoin="round"
|
||||
class="w-5 h-5 text-primary"
|
||||
>
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
||||
<path d="M9 10h.01M15 10h.01"/>
|
||||
<path
|
||||
d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
|
||||
/>
|
||||
<path d="M9 10h.01M15 10h.01" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Avatar -->
|
||||
<div class="relative flex-shrink-0">
|
||||
<UserAvatar
|
||||
avatar={usuario.avatar}
|
||||
fotoPerfilUrl={usuario.fotoPerfilUrl}
|
||||
nome={usuario.nome}
|
||||
size="md"
|
||||
/>
|
||||
<!-- Status badge -->
|
||||
<div class="absolute bottom-0 right-0">
|
||||
<UserStatusBadge status={usuario.statusPresenca || "offline"} size="sm" />
|
||||
<!-- Avatar -->
|
||||
<div class="relative flex-shrink-0">
|
||||
<UserAvatar
|
||||
avatar={usuario.avatar}
|
||||
fotoPerfilUrl={usuario.fotoPerfilUrl}
|
||||
nome={usuario.nome}
|
||||
size="md"
|
||||
/>
|
||||
<!-- Status badge -->
|
||||
<div class="absolute bottom-0 right-0">
|
||||
<UserStatusBadge
|
||||
status={usuario.statusPresenca || "offline"}
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Conteúdo -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center justify-between mb-1">
|
||||
<p class="font-semibold text-base-content truncate">
|
||||
{usuario.nome}
|
||||
</p>
|
||||
<span class="text-xs px-2 py-0.5 rounded-full {
|
||||
usuario.statusPresenca === 'online' ? 'bg-success/20 text-success' :
|
||||
usuario.statusPresenca === 'ausente' ? 'bg-warning/20 text-warning' :
|
||||
usuario.statusPresenca === 'em_reuniao' ? 'bg-error/20 text-error' :
|
||||
'bg-base-300 text-base-content/50'
|
||||
}">
|
||||
{getStatusLabel(usuario.statusPresenca)}
|
||||
</span>
|
||||
<!-- Conteúdo -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center justify-between mb-1">
|
||||
<p class="font-semibold text-base-content truncate">
|
||||
{usuario.nome}
|
||||
</p>
|
||||
<span
|
||||
class="text-xs px-2 py-0.5 rounded-full {usuario.statusPresenca ===
|
||||
'online'
|
||||
? 'bg-success/20 text-success'
|
||||
: usuario.statusPresenca === 'ausente'
|
||||
? 'bg-warning/20 text-warning'
|
||||
: usuario.statusPresenca === 'em_reuniao'
|
||||
? 'bg-error/20 text-error'
|
||||
: 'bg-base-300 text-base-content/50'}"
|
||||
>
|
||||
{getStatusLabel(usuario.statusPresenca)}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<p class="text-sm text-base-content/70 truncate">
|
||||
{usuario.statusMensagem || usuario.email}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<p class="text-sm text-base-content/70 truncate">
|
||||
{usuario.statusMensagem || usuario.email}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
{:else if !usuarios?.data}
|
||||
<!-- Loading -->
|
||||
<div class="flex items-center justify-center h-full">
|
||||
<span class="loading loading-spinner loading-lg"></span>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Nenhum usuário encontrado -->
|
||||
<div class="flex flex-col items-center justify-center h-full text-center px-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-16 h-16 text-base-content/30 mb-4"
|
||||
</button>
|
||||
{/each}
|
||||
{:else if !usuarios?.data}
|
||||
<!-- Loading -->
|
||||
<div class="flex items-center justify-center h-full">
|
||||
<span class="loading loading-spinner loading-lg"></span>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Nenhum usuário encontrado -->
|
||||
<div
|
||||
class="flex flex-col items-center justify-center h-full text-center px-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z"
|
||||
/>
|
||||
</svg>
|
||||
<p class="text-base-content/70">Nenhum usuário encontrado</p>
|
||||
</div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-16 h-16 text-base-content/30 mb-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z"
|
||||
/>
|
||||
</svg>
|
||||
<p class="text-base-content/70">Nenhum usuário encontrado</p>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<!-- Lista de conversas (grupos e salas) -->
|
||||
@@ -341,23 +388,48 @@
|
||||
{#each conversasFiltradas() as conversa (conversa._id)}
|
||||
<button
|
||||
type="button"
|
||||
class="w-full text-left px-4 py-3 hover:bg-base-200 border-b border-base-300 transition-colors flex items-center gap-3 {processando ? 'opacity-50 cursor-wait' : 'cursor-pointer'}"
|
||||
class="w-full text-left px-4 py-3 hover:bg-base-200 border-b border-base-300 transition-colors flex items-center gap-3 {processando
|
||||
? 'opacity-50 cursor-wait'
|
||||
: 'cursor-pointer'}"
|
||||
onclick={() => handleClickConversa(conversa)}
|
||||
disabled={processando}
|
||||
>
|
||||
<!-- Ícone de grupo/sala -->
|
||||
<div class="flex-shrink-0 w-10 h-10 rounded-xl flex items-center justify-center transition-all duration-300 hover:scale-110 {
|
||||
conversa.tipo === 'sala_reuniao'
|
||||
? 'bg-gradient-to-br from-blue-500/20 to-purple-500/20 border border-blue-300/30'
|
||||
: 'bg-gradient-to-br from-primary/20 to-secondary/20 border border-primary/30'
|
||||
}">
|
||||
<div
|
||||
class="flex-shrink-0 w-10 h-10 rounded-xl flex items-center justify-center transition-all duration-300 hover:scale-110 {conversa.tipo ===
|
||||
'sala_reuniao'
|
||||
? 'bg-linear-to-br from-blue-500/20 to-purple-500/20 border border-blue-300/30'
|
||||
: 'bg-linear-to-br from-primary/20 to-secondary/20 border border-primary/30'}"
|
||||
>
|
||||
{#if conversa.tipo === "sala_reuniao"}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 text-blue-500">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5 text-blue-500"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z"
|
||||
/>
|
||||
</svg>
|
||||
{:else}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 text-primary">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5 text-primary"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -366,21 +438,34 @@
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center justify-between mb-1">
|
||||
<p class="font-semibold text-base-content truncate">
|
||||
{conversa.nome || (conversa.tipo === "sala_reuniao" ? "Sala sem nome" : "Grupo sem nome")}
|
||||
{conversa.nome ||
|
||||
(conversa.tipo === "sala_reuniao"
|
||||
? "Sala sem nome"
|
||||
: "Grupo sem nome")}
|
||||
</p>
|
||||
{#if conversa.naoLidas > 0}
|
||||
<span class="badge badge-primary badge-sm">{conversa.naoLidas}</span>
|
||||
<span class="badge badge-primary badge-sm"
|
||||
>{conversa.naoLidas}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs px-2 py-0.5 rounded-full {
|
||||
conversa.tipo === 'sala_reuniao' ? 'bg-blue-500/20 text-blue-500' : 'bg-primary/20 text-primary'
|
||||
}">
|
||||
{conversa.tipo === "sala_reuniao" ? "👑 Sala de Reunião" : "👥 Grupo"}
|
||||
<span
|
||||
class="text-xs px-2 py-0.5 rounded-full {conversa.tipo ===
|
||||
'sala_reuniao'
|
||||
? 'bg-blue-500/20 text-blue-500'
|
||||
: 'bg-primary/20 text-primary'}"
|
||||
>
|
||||
{conversa.tipo === "sala_reuniao"
|
||||
? "👑 Sala de Reunião"
|
||||
: "👥 Grupo"}
|
||||
</span>
|
||||
{#if conversa.participantesInfo}
|
||||
<span class="text-xs text-base-content/50">
|
||||
{conversa.participantesInfo.length} participante{conversa.participantesInfo.length !== 1 ? 's' : ''}
|
||||
{conversa.participantesInfo.length} participante{conversa
|
||||
.participantesInfo.length !== 1
|
||||
? "s"
|
||||
: ""}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -394,12 +479,29 @@
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Nenhuma conversa encontrada -->
|
||||
<div class="flex flex-col items-center justify-center h-full text-center px-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-16 h-16 text-base-content/30 mb-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155" />
|
||||
<div
|
||||
class="flex flex-col items-center justify-center h-full text-center px-4"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-16 h-16 text-base-content/30 mb-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155"
|
||||
/>
|
||||
</svg>
|
||||
<p class="text-base-content/70 font-medium mb-2">Nenhuma conversa encontrada</p>
|
||||
<p class="text-sm text-base-content/50">Crie um grupo ou sala de reunião para começar</p>
|
||||
<p class="text-base-content/70 font-medium mb-2">
|
||||
Nenhuma conversa encontrada
|
||||
</p>
|
||||
<p class="text-sm text-base-content/50">
|
||||
Crie um grupo ou sala de reunião para começar
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -6,7 +6,17 @@
|
||||
import { ptBR } from "date-fns/locale";
|
||||
import { onMount } from "svelte";
|
||||
import { authStore } from "$lib/stores/auth.svelte";
|
||||
import { Bell, Mail, AtSign, Users, Calendar, Clock, BellOff, Trash2, X } from "lucide-svelte";
|
||||
import {
|
||||
Bell,
|
||||
Mail,
|
||||
AtSign,
|
||||
Users,
|
||||
Calendar,
|
||||
Clock,
|
||||
BellOff,
|
||||
Trash2,
|
||||
X,
|
||||
} from "lucide-svelte";
|
||||
|
||||
// Queries e Client
|
||||
const client = useConvexClient();
|
||||
@@ -18,31 +28,46 @@
|
||||
});
|
||||
|
||||
let modalOpen = $state(false);
|
||||
let notificacoesFerias = $state<Array<{ _id: string; mensagem: string; tipo: string; _creationTime: number }>>([]);
|
||||
let notificacoesAusencias = $state<Array<{ _id: string; mensagem: string; tipo: string; _creationTime: number }>>([]);
|
||||
let notificacoesFerias = $state<
|
||||
Array<{
|
||||
_id: string;
|
||||
mensagem: string;
|
||||
tipo: string;
|
||||
_creationTime: number;
|
||||
}>
|
||||
>([]);
|
||||
let notificacoesAusencias = $state<
|
||||
Array<{
|
||||
_id: string;
|
||||
mensagem: string;
|
||||
tipo: string;
|
||||
_creationTime: number;
|
||||
}>
|
||||
>([]);
|
||||
let limpandoNotificacoes = $state(false);
|
||||
|
||||
// Helpers para obter valores das queries
|
||||
const count = $derived(
|
||||
(typeof countQuery === "number" ? countQuery : countQuery?.data) ?? 0
|
||||
(typeof countQuery === "number" ? countQuery : countQuery?.data) ?? 0,
|
||||
);
|
||||
const todasNotificacoes = $derived(
|
||||
(Array.isArray(todasNotificacoesQuery)
|
||||
? todasNotificacoesQuery
|
||||
: todasNotificacoesQuery?.data) ?? []
|
||||
: todasNotificacoesQuery?.data) ?? [],
|
||||
);
|
||||
|
||||
|
||||
// Separar notificações lidas e não lidas
|
||||
const notificacoesNaoLidas = $derived(
|
||||
todasNotificacoes.filter((n) => !n.lida)
|
||||
);
|
||||
const notificacoesLidas = $derived(
|
||||
todasNotificacoes.filter((n) => n.lida)
|
||||
todasNotificacoes.filter((n) => !n.lida),
|
||||
);
|
||||
const notificacoesLidas = $derived(todasNotificacoes.filter((n) => n.lida));
|
||||
|
||||
// Atualizar contador no store
|
||||
$effect(() => {
|
||||
const totalNotificacoes = count + (notificacoesFerias?.length || 0) + (notificacoesAusencias?.length || 0);
|
||||
const totalNotificacoes =
|
||||
count +
|
||||
(notificacoesFerias?.length || 0) +
|
||||
(notificacoesAusencias?.length || 0);
|
||||
notificacoesCount.set(totalNotificacoes);
|
||||
});
|
||||
|
||||
@@ -56,7 +81,7 @@
|
||||
api.ferias.obterNotificacoesNaoLidas,
|
||||
{
|
||||
usuarioId: usuarioStore.usuario._id,
|
||||
}
|
||||
},
|
||||
);
|
||||
notificacoesFerias = notifsFerias || [];
|
||||
}
|
||||
@@ -76,14 +101,20 @@
|
||||
api.ausencias.obterNotificacoesNaoLidas,
|
||||
{
|
||||
usuarioId: usuarioStore.usuario._id,
|
||||
}
|
||||
},
|
||||
);
|
||||
notificacoesAusencias = notifsAusencias || [];
|
||||
} catch (queryError: unknown) {
|
||||
// Silenciar erro se a função não estiver disponível ainda (Convex não sincronizado)
|
||||
const errorMessage = queryError instanceof Error ? queryError.message : String(queryError);
|
||||
const errorMessage =
|
||||
queryError instanceof Error
|
||||
? queryError.message
|
||||
: String(queryError);
|
||||
if (!errorMessage.includes("Could not find public function")) {
|
||||
console.error("Erro ao buscar notificações de ausências:", queryError);
|
||||
console.error(
|
||||
"Erro ao buscar notificações de ausências:",
|
||||
queryError,
|
||||
);
|
||||
}
|
||||
notificacoesAusencias = [];
|
||||
}
|
||||
@@ -201,7 +232,10 @@
|
||||
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
const target = event.target as HTMLElement;
|
||||
if (!target.closest(".notification-popup") && !target.closest(".notification-bell")) {
|
||||
if (
|
||||
!target.closest(".notification-popup") &&
|
||||
!target.closest(".notification-bell")
|
||||
) {
|
||||
modalOpen = false;
|
||||
}
|
||||
}
|
||||
@@ -233,7 +267,7 @@
|
||||
>
|
||||
<!-- Efeito de brilho no hover -->
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-br from-white/0 to-white/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||
class="absolute inset-0 bg-linear-to-br from-white/0 to-white/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||
></div>
|
||||
|
||||
<!-- Anel de pulso sutil -->
|
||||
@@ -271,52 +305,59 @@
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<!-- Popup Flutuante de Notificações -->
|
||||
{#if modalOpen}
|
||||
<div class="notification-popup fixed right-4 top-24 z-[100] w-[calc(100vw-2rem)] max-w-2xl max-h-[calc(100vh-7rem)] flex flex-col bg-base-100 rounded-2xl shadow-2xl border border-base-300 overflow-hidden backdrop-blur-sm" style="animation: slideDown 0.2s ease-out;">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between px-6 py-4 border-b border-base-300 bg-gradient-to-r from-primary/5 to-primary/10">
|
||||
<h3 class="text-2xl font-bold text-primary">Notificações</h3>
|
||||
<div class="flex items-center gap-2">
|
||||
{#if notificacoesNaoLidas.length > 0}
|
||||
<!-- Popup Flutuante de Notificações -->
|
||||
{#if modalOpen}
|
||||
<div
|
||||
class="notification-popup fixed right-4 top-24 z-[100] w-[calc(100vw-2rem)] max-w-2xl max-h-[calc(100vh-7rem)] flex flex-col bg-base-100 rounded-2xl shadow-2xl border border-base-300 overflow-hidden backdrop-blur-sm"
|
||||
style="animation: slideDown 0.2s ease-out;"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div
|
||||
class="flex items-center justify-between px-6 py-4 border-b border-base-300 bg-linear-to-r from-primary/5 to-primary/10"
|
||||
>
|
||||
<h3 class="text-2xl font-bold text-primary">Notificações</h3>
|
||||
<div class="flex items-center gap-2">
|
||||
{#if notificacoesNaoLidas.length > 0}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-ghost"
|
||||
onclick={handleLimparNotificacoesNaoLidas}
|
||||
disabled={limpandoNotificacoes}
|
||||
>
|
||||
<Trash2 class="w-4 h-4" />
|
||||
Limpar não lidas
|
||||
</button>
|
||||
{/if}
|
||||
{#if todasNotificacoes.length > 0}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-error btn-outline"
|
||||
onclick={handleLimparTodasNotificacoes}
|
||||
disabled={limpandoNotificacoes}
|
||||
>
|
||||
<Trash2 class="w-4 h-4" />
|
||||
Limpar todas
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-ghost"
|
||||
onclick={handleLimparNotificacoesNaoLidas}
|
||||
disabled={limpandoNotificacoes}
|
||||
class="btn btn-sm btn-circle btn-ghost"
|
||||
onclick={closeModal}
|
||||
>
|
||||
<Trash2 class="w-4 h-4" />
|
||||
Limpar não lidas
|
||||
<X class="w-5 h-5" />
|
||||
</button>
|
||||
{/if}
|
||||
{#if todasNotificacoes.length > 0}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-error btn-outline"
|
||||
onclick={handleLimparTodasNotificacoes}
|
||||
disabled={limpandoNotificacoes}
|
||||
>
|
||||
<Trash2 class="w-4 h-4" />
|
||||
Limpar todas
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-circle btn-ghost"
|
||||
onclick={closeModal}
|
||||
>
|
||||
<X class="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista de notificações -->
|
||||
<div class="flex-1 overflow-y-auto px-2 py-4">
|
||||
<!-- Lista de notificações -->
|
||||
<div class="flex-1 overflow-y-auto px-2 py-4">
|
||||
{#if todasNotificacoes.length > 0 || notificacoesFerias.length > 0 || notificacoesAusencias.length > 0}
|
||||
<!-- Notificações não lidas -->
|
||||
{#if notificacoesNaoLidas.length > 0}
|
||||
<div class="mb-4">
|
||||
<h4 class="text-sm font-semibold text-primary mb-2 px-2">Não lidas</h4>
|
||||
<h4 class="text-sm font-semibold text-primary mb-2 px-2">
|
||||
Não lidas
|
||||
</h4>
|
||||
{#each notificacoesNaoLidas as notificacao (notificacao._id)}
|
||||
<button
|
||||
type="button"
|
||||
@@ -329,7 +370,10 @@
|
||||
{#if notificacao.tipo === "nova_mensagem"}
|
||||
<Mail class="w-5 h-5 text-primary" strokeWidth={1.5} />
|
||||
{:else if notificacao.tipo === "mencao"}
|
||||
<AtSign class="w-5 h-5 text-warning" strokeWidth={1.5} />
|
||||
<AtSign
|
||||
class="w-5 h-5 text-warning"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
{:else}
|
||||
<Users class="w-5 h-5 text-info" strokeWidth={1.5} />
|
||||
{/if}
|
||||
@@ -341,21 +385,27 @@
|
||||
<p class="text-sm font-semibold text-primary">
|
||||
{notificacao.remetente.nome}
|
||||
</p>
|
||||
<p class="text-xs text-base-content/70 mt-1 line-clamp-2">
|
||||
<p
|
||||
class="text-xs text-base-content/70 mt-1 line-clamp-2"
|
||||
>
|
||||
{notificacao.descricao}
|
||||
</p>
|
||||
{:else if notificacao.tipo === "mencao" && notificacao.remetente}
|
||||
<p class="text-sm font-semibold text-warning">
|
||||
{notificacao.remetente.nome} mencionou você
|
||||
</p>
|
||||
<p class="text-xs text-base-content/70 mt-1 line-clamp-2">
|
||||
<p
|
||||
class="text-xs text-base-content/70 mt-1 line-clamp-2"
|
||||
>
|
||||
{notificacao.descricao}
|
||||
</p>
|
||||
{:else}
|
||||
<p class="text-sm font-semibold text-base-content">
|
||||
{notificacao.titulo}
|
||||
</p>
|
||||
<p class="text-xs text-base-content/70 mt-1 line-clamp-2">
|
||||
<p
|
||||
class="text-xs text-base-content/70 mt-1 line-clamp-2"
|
||||
>
|
||||
{notificacao.descricao}
|
||||
</p>
|
||||
{/if}
|
||||
@@ -377,7 +427,9 @@
|
||||
<!-- Notificações lidas -->
|
||||
{#if notificacoesLidas.length > 0}
|
||||
<div class="mb-4">
|
||||
<h4 class="text-sm font-semibold text-base-content/60 mb-2 px-2">Lidas</h4>
|
||||
<h4 class="text-sm font-semibold text-base-content/60 mb-2 px-2">
|
||||
Lidas
|
||||
</h4>
|
||||
{#each notificacoesLidas as notificacao (notificacao._id)}
|
||||
<button
|
||||
type="button"
|
||||
@@ -388,9 +440,15 @@
|
||||
<!-- Ícone -->
|
||||
<div class="flex-shrink-0 mt-1">
|
||||
{#if notificacao.tipo === "nova_mensagem"}
|
||||
<Mail class="w-5 h-5 text-primary/60" strokeWidth={1.5} />
|
||||
<Mail
|
||||
class="w-5 h-5 text-primary/60"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
{:else if notificacao.tipo === "mencao"}
|
||||
<AtSign class="w-5 h-5 text-warning/60" strokeWidth={1.5} />
|
||||
<AtSign
|
||||
class="w-5 h-5 text-warning/60"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
{:else}
|
||||
<Users class="w-5 h-5 text-info/60" strokeWidth={1.5} />
|
||||
{/if}
|
||||
@@ -402,21 +460,27 @@
|
||||
<p class="text-sm font-medium text-primary/70">
|
||||
{notificacao.remetente.nome}
|
||||
</p>
|
||||
<p class="text-xs text-base-content/60 mt-1 line-clamp-2">
|
||||
<p
|
||||
class="text-xs text-base-content/60 mt-1 line-clamp-2"
|
||||
>
|
||||
{notificacao.descricao}
|
||||
</p>
|
||||
{:else if notificacao.tipo === "mencao" && notificacao.remetente}
|
||||
<p class="text-sm font-medium text-warning/70">
|
||||
{notificacao.remetente.nome} mencionou você
|
||||
</p>
|
||||
<p class="text-xs text-base-content/60 mt-1 line-clamp-2">
|
||||
<p
|
||||
class="text-xs text-base-content/60 mt-1 line-clamp-2"
|
||||
>
|
||||
{notificacao.descricao}
|
||||
</p>
|
||||
{:else}
|
||||
<p class="text-sm font-medium text-base-content/70">
|
||||
{notificacao.titulo}
|
||||
</p>
|
||||
<p class="text-xs text-base-content/60 mt-1 line-clamp-2">
|
||||
<p
|
||||
class="text-xs text-base-content/60 mt-1 line-clamp-2"
|
||||
>
|
||||
{notificacao.descricao}
|
||||
</p>
|
||||
{/if}
|
||||
@@ -433,7 +497,9 @@
|
||||
<!-- Notificações de Férias -->
|
||||
{#if notificacoesFerias.length > 0}
|
||||
<div class="mb-4">
|
||||
<h4 class="text-sm font-semibold text-purple-600 mb-2 px-2">Férias</h4>
|
||||
<h4 class="text-sm font-semibold text-purple-600 mb-2 px-2">
|
||||
Férias
|
||||
</h4>
|
||||
{#each notificacoesFerias as notificacao (notificacao._id)}
|
||||
<button
|
||||
type="button"
|
||||
@@ -443,7 +509,10 @@
|
||||
<div class="flex items-start gap-3">
|
||||
<!-- Ícone -->
|
||||
<div class="flex-shrink-0 mt-1">
|
||||
<Calendar class="w-5 h-5 text-purple-600" strokeWidth={2} />
|
||||
<Calendar
|
||||
class="w-5 h-5 text-purple-600"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Conteúdo -->
|
||||
@@ -469,12 +538,15 @@
|
||||
<!-- Notificações de Ausências -->
|
||||
{#if notificacoesAusencias.length > 0}
|
||||
<div class="mb-4">
|
||||
<h4 class="text-sm font-semibold text-orange-600 mb-2 px-2">Ausências</h4>
|
||||
<h4 class="text-sm font-semibold text-orange-600 mb-2 px-2">
|
||||
Ausências
|
||||
</h4>
|
||||
{#each notificacoesAusencias as notificacao (notificacao._id)}
|
||||
<button
|
||||
type="button"
|
||||
class="w-full text-left px-4 py-3 hover:bg-base-200 rounded-lg transition-colors mb-2 border-l-4 border-orange-600"
|
||||
onclick={() => handleClickNotificacaoAusencias(notificacao._id)}
|
||||
onclick={() =>
|
||||
handleClickNotificacaoAusencias(notificacao._id)}
|
||||
>
|
||||
<div class="flex items-start gap-3">
|
||||
<!-- Ícone -->
|
||||
@@ -504,30 +576,37 @@
|
||||
{:else}
|
||||
<!-- Sem notificações -->
|
||||
<div class="px-4 py-12 text-center text-base-content/50">
|
||||
<BellOff class="w-16 h-16 mx-auto mb-4 opacity-50" strokeWidth={1.5} />
|
||||
<BellOff
|
||||
class="w-16 h-16 mx-auto mb-4 opacity-50"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<p class="text-base font-medium">Nenhuma notificação</p>
|
||||
<p class="text-sm mt-1">Você está em dia!</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Footer com estatísticas -->
|
||||
{#if todasNotificacoes.length > 0 || notificacoesFerias.length > 0 || notificacoesAusencias.length > 0}
|
||||
<div class="px-6 py-4 border-t border-base-300 bg-base-200/50">
|
||||
<div class="flex items-center justify-between text-xs text-base-content/60">
|
||||
<span>
|
||||
Total: {todasNotificacoes.length + notificacoesFerias.length + notificacoesAusencias.length} notificações
|
||||
</span>
|
||||
{#if notificacoesNaoLidas.length > 0}
|
||||
<span class="text-primary font-semibold">
|
||||
{notificacoesNaoLidas.length} não lidas
|
||||
<!-- Footer com estatísticas -->
|
||||
{#if todasNotificacoes.length > 0 || notificacoesFerias.length > 0 || notificacoesAusencias.length > 0}
|
||||
<div class="px-6 py-4 border-t border-base-300 bg-base-200/50">
|
||||
<div
|
||||
class="flex items-center justify-between text-xs text-base-content/60"
|
||||
>
|
||||
<span>
|
||||
Total: {todasNotificacoes.length +
|
||||
notificacoesFerias.length +
|
||||
notificacoesAusencias.length} notificações
|
||||
</span>
|
||||
{/if}
|
||||
{#if notificacoesNaoLidas.length > 0}
|
||||
<span class="text-primary font-semibold">
|
||||
{notificacoesNaoLidas.length} não lidas
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -582,4 +661,3 @@
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user