feat: implement vacation management system with request approval, notification handling, and employee training tracking; enhance UI components for improved user experience
This commit is contained in:
@@ -8,16 +8,42 @@
|
||||
|
||||
// Queries e Client
|
||||
const client = useConvexClient();
|
||||
const notificacoes = useQuery(api.chat.obterNotificacoes, { apenasPendentes: true });
|
||||
const count = useQuery(api.chat.contarNotificacoesNaoLidas, {});
|
||||
const notificacoesQuery = useQuery(api.chat.obterNotificacoes, { apenasPendentes: true });
|
||||
const countQuery = useQuery(api.chat.contarNotificacoesNaoLidas, {});
|
||||
|
||||
let dropdownOpen = $state(false);
|
||||
let notificacoesFerias = $state<any[]>([]);
|
||||
|
||||
// Helpers para obter valores das queries
|
||||
const count = $derived((typeof countQuery === 'number' ? countQuery : countQuery?.data) ?? 0);
|
||||
const notificacoes = $derived((Array.isArray(notificacoesQuery) ? notificacoesQuery : notificacoesQuery?.data) ?? []);
|
||||
|
||||
// Atualizar contador no store
|
||||
$effect(() => {
|
||||
if (count !== undefined) {
|
||||
notificacoesCount.set(count);
|
||||
const totalNotificacoes = count + (notificacoesFerias?.length || 0);
|
||||
notificacoesCount.set(totalNotificacoes);
|
||||
});
|
||||
|
||||
// Buscar notificações de férias
|
||||
async function buscarNotificacoesFerias() {
|
||||
try {
|
||||
const usuarioStore = await import("$lib/stores/auth.svelte").then(m => m.authStore);
|
||||
if (usuarioStore.usuario?._id) {
|
||||
const notifsFerias = await client.query(api.ferias.obterNotificacoesNaoLidas, {
|
||||
usuarioId: usuarioStore.usuario._id as any,
|
||||
});
|
||||
notificacoesFerias = notifsFerias || [];
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Erro ao buscar notificações de férias:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Atualizar notificações de férias periodicamente
|
||||
$effect(() => {
|
||||
buscarNotificacoesFerias();
|
||||
const interval = setInterval(buscarNotificacoesFerias, 30000); // A cada 30s
|
||||
return () => clearInterval(interval);
|
||||
});
|
||||
|
||||
function formatarTempo(timestamp: number): string {
|
||||
@@ -33,7 +59,12 @@
|
||||
|
||||
async function handleMarcarTodasLidas() {
|
||||
await client.mutation(api.chat.marcarTodasNotificacoesLidas, {});
|
||||
// Marcar todas as notificações de férias como lidas
|
||||
for (const notif of notificacoesFerias) {
|
||||
await client.mutation(api.ferias.marcarComoLida, { notificacaoId: notif._id });
|
||||
}
|
||||
dropdownOpen = false;
|
||||
await buscarNotificacoesFerias();
|
||||
}
|
||||
|
||||
async function handleClickNotificacao(notificacaoId: string) {
|
||||
@@ -41,6 +72,14 @@
|
||||
dropdownOpen = false;
|
||||
}
|
||||
|
||||
async function handleClickNotificacaoFerias(notificacaoId: string) {
|
||||
await client.mutation(api.ferias.marcarComoLida, { notificacaoId: notificacaoId as any });
|
||||
await buscarNotificacoesFerias();
|
||||
dropdownOpen = false;
|
||||
// Redirecionar para a página de férias
|
||||
window.location.href = "/recursos-humanos/ferias";
|
||||
}
|
||||
|
||||
function toggleDropdown() {
|
||||
dropdownOpen = !dropdownOpen;
|
||||
}
|
||||
@@ -101,12 +140,13 @@
|
||||
</svg>
|
||||
|
||||
<!-- Badge premium com gradiente -->
|
||||
{#if count && count > 0}
|
||||
{#if count + (notificacoesFerias?.length || 0) > 0}
|
||||
{@const totalCount = count + (notificacoesFerias?.length || 0)}
|
||||
<span
|
||||
class="absolute -top-1 -right-1 flex h-6 w-6 items-center justify-center rounded-full bg-gradient-to-br from-red-500 via-error to-red-600 text-white text-[10px] font-black shadow-xl ring-2 ring-white z-20"
|
||||
style="animation: badge-bounce 2s ease-in-out infinite;"
|
||||
>
|
||||
{count > 9 ? "9+" : count}
|
||||
{totalCount > 9 ? "9+" : totalCount}
|
||||
</span>
|
||||
{/if}
|
||||
</button>
|
||||
@@ -119,7 +159,7 @@
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between px-4 py-2 border-b border-base-300">
|
||||
<h3 class="text-lg font-semibold">Notificações</h3>
|
||||
{#if count && count > 0}
|
||||
{#if count > 0}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-xs"
|
||||
@@ -132,7 +172,7 @@
|
||||
|
||||
<!-- Lista de notificações -->
|
||||
<div class="py-2">
|
||||
{#if notificacoes && notificacoes.length > 0}
|
||||
{#if notificacoes.length > 0}
|
||||
{#each notificacoes.slice(0, 10) as notificacao (notificacao._id)}
|
||||
<button
|
||||
type="button"
|
||||
@@ -212,7 +252,48 @@
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
{:else}
|
||||
{/if}
|
||||
|
||||
<!-- Notificações de Férias -->
|
||||
{#if notificacoesFerias.length > 0}
|
||||
{#if notificacoes.length > 0}
|
||||
<div class="divider my-2 text-xs">Férias</div>
|
||||
{/if}
|
||||
{#each notificacoesFerias.slice(0, 5) as notificacao (notificacao._id)}
|
||||
<button
|
||||
type="button"
|
||||
class="w-full text-left px-4 py-3 hover:bg-base-200 rounded-lg transition-colors"
|
||||
onclick={() => handleClickNotificacaoFerias(notificacao._id)}
|
||||
>
|
||||
<div class="flex items-start gap-3">
|
||||
<!-- Ícone -->
|
||||
<div class="flex-shrink-0 mt-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-purple-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Conteúdo -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm font-medium text-base-content">
|
||||
{notificacao.mensagem}
|
||||
</p>
|
||||
<p class="text-xs text-base-content/50 mt-1">
|
||||
{formatarTempo(notificacao._creationTime)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Badge -->
|
||||
<div class="flex-shrink-0">
|
||||
<div class="badge badge-primary badge-xs"></div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<!-- Sem notificações -->
|
||||
{#if notificacoes.length === 0 && notificacoesFerias.length === 0}
|
||||
<div class="px-4 py-8 text-center text-base-content/50">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
Reference in New Issue
Block a user