feat: enhance LGPD request handling with email notifications and response templates; update frontend filters for improved user experience

This commit is contained in:
2025-12-04 05:13:43 -03:00
parent 4a662c08a0
commit a3d9e782af
4 changed files with 333 additions and 46 deletions

View File

@@ -46,7 +46,7 @@
} from '$lib/utils/chamados';
import { useConvexWithAuth } from '$lib/hooks/useConvexWithAuth';
import type { Doc } from '@sgse-app/backend/convex/_generated/dataModel';
import { temasDisponiveis, aplicarTema, type Tema } from '$lib/utils/temas';
import { temasDisponiveis, aplicarTema } from '$lib/utils/temas';
const client = useConvexClient();
// @ts-expect-error - Convex types issue with getCurrentUser
@@ -128,6 +128,9 @@
const funcionarioIdDisponivel = $derived(currentUser?.data?.funcionarioId ?? null);
const gestorIdDisponivel = $derived(currentUser?.data?._id ?? null);
// Qualquer usuário com funcionarioId é considerado funcionário
const isFuncionario = $derived(!!funcionarioIdDisponivel);
// Verificar autenticação antes de executar queries
const usuarioAutenticado = $derived(
currentUser?.data !== null && currentUser?.data !== undefined
@@ -818,8 +821,8 @@
<FileCheck class="h-5 w-5" strokeWidth={2} />
Meus Chamados
</button>
{#if ehGestor}
{#if isFuncionario}
<!-- Funcionário: solicitar férias -->
<button
type="button"
role="tab"
@@ -830,6 +833,7 @@
Minhas Férias
</button>
<!-- Funcionário: solicitar ausências -->
<button
type="button"
role="tab"
@@ -839,41 +843,42 @@
<Clock class="h-5 w-5" strokeWidth={2} />
Minhas Ausências
</button>
{/if}
{#if ehGestor}
<button
type="button"
role="tab"
class={`tab tab-lg gap-2 font-semibold transition-all duration-300 ${abaAtiva === 'aprovar-ferias' ? 'tab-active scale-105 bg-gradient-to-r from-green-600 to-emerald-600 text-white shadow-lg' : 'hover:bg-base-100'}`}
onclick={() => (abaAtiva = 'aprovar-ferias')}
>
<CheckCircle2 class="h-5 w-5" strokeWidth={2} />
Aprovar Férias
{#if (solicitacoesSubordinados || []).filter((s) => s.status === 'aguardando_aprovacao').length > 0}
<span class="badge badge-error badge-sm ml-1 animate-pulse">
{(solicitacoesSubordinados || []).filter(
(s) => s.status === 'aguardando_aprovacao'
).length}
</span>
{/if}
</button>
{#if ehGestor}
<!-- Gestor: aprovar férias -->
<button
type="button"
role="tab"
class={`tab tab-lg gap-2 font-semibold transition-all duration-300 ${abaAtiva === 'aprovar-ferias' ? 'tab-active scale-105 bg-gradient-to-r from-green-600 to-emerald-600 text-white shadow-lg' : 'hover:bg-base-100'}`}
onclick={() => (abaAtiva = 'aprovar-ferias')}
>
<CheckCircle2 class="h-5 w-5" strokeWidth={2} />
Aprovar Férias
{#if (solicitacoesSubordinados || []).filter((s) => s.status === 'aguardando_aprovacao').length > 0}
<span class="badge badge-error badge-sm ml-1 animate-pulse">
{(solicitacoesSubordinados || []).filter((s) => s.status === 'aguardando_aprovacao')
.length}
</span>
{/if}
</button>
<button
type="button"
role="tab"
class={`tab tab-lg gap-2 font-semibold transition-all duration-300 ${abaAtiva === 'aprovar-ausencias' ? 'tab-active scale-105 bg-gradient-to-r from-orange-600 to-amber-600 text-white shadow-lg' : 'hover:bg-base-100'}`}
onclick={() => (abaAtiva = 'aprovar-ausencias')}
>
<Clock class="h-5 w-5" strokeWidth={2} />
Aprovar Ausências
{#if (ausenciasSubordinados || []).filter((a) => a.status === 'aguardando_aprovacao').length > 0}
<span class="badge badge-error badge-sm ml-1 animate-pulse">
{(ausenciasSubordinados || []).filter((a) => a.status === 'aguardando_aprovacao')
.length}
</span>
{/if}
</button>
{/if}
<!-- Gestor: aprovar ausências -->
<button
type="button"
role="tab"
class={`tab tab-lg gap-2 font-semibold transition-all duration-300 ${abaAtiva === 'aprovar-ausencias' ? 'tab-active scale-105 bg-gradient-to-r from-orange-600 to-amber-600 text-white shadow-lg' : 'hover:bg-base-100'}`}
onclick={() => (abaAtiva = 'aprovar-ausencias')}
>
<Clock class="h-5 w-5" strokeWidth={2} />
Aprovar Ausências
{#if (ausenciasSubordinados || []).filter((a) => a.status === 'aguardando_aprovacao').length > 0}
<span class="badge badge-error badge-sm ml-1 animate-pulse">
{(ausenciasSubordinados || []).filter((a) => a.status === 'aguardando_aprovacao')
.length}
</span>
{/if}
</button>
{/if}
<button
@@ -1217,7 +1222,7 @@
>
{#if setoresQuery?.data && setoresQuery.data.length > 0}
<div class="mt-2 flex flex-wrap gap-2">
{#each setoresQuery.data as setor}
{#each setoresQuery.data as setor (setor._id)}
<div
class="badge badge-lg font-semibold shadow-sm"
style="background-color: rgba(20, 184, 166, 0.1); border-color: rgba(20, 184, 166, 0.3); color: rgb(15, 118, 110);"
@@ -1229,8 +1234,6 @@
</div>
{/each}
</div>
{:else if setoresQuery?.isLoading}
<p class="text-base-content/50 mt-1 text-sm">Carregando...</p>
{:else}
<p class="text-base-content/50 mt-1 text-sm">Nenhum setor atribuído</p>
{/if}

View File

@@ -22,18 +22,19 @@
import { ptBR } from 'date-fns/locale';
import { toast } from 'svelte-sonner';
type StatusFiltro = 'pendente' | 'em_analise' | 'concluida' | 'rejeitada' | null;
type StatusFiltro = '' | 'pendente' | 'em_analise' | 'concluida' | 'rejeitada';
type TipoFiltro =
| ''
| 'acesso'
| 'correcao'
| 'exclusao'
| 'portabilidade'
| 'revogacao_consentimento'
| 'informacao_compartilhamento'
| null;
| 'informacao_compartilhamento';
let statusFiltro = $state<StatusFiltro>(null);
let tipoFiltro = $state<TipoFiltro>(null);
// '' = Todos (sem filtro)
let statusFiltro = $state<StatusFiltro>('');
let tipoFiltro = $state<TipoFiltro>('');
let termoBusca = $state('');
const client = useConvexClient();
@@ -221,7 +222,7 @@
<span class="label-text font-semibold">Status</span>
</label>
<select bind:value={statusFiltro} class="select select-bordered">
<option value={null}>Todos</option>
<option value="">Todos</option>
<option value="pendente">Pendente</option>
<option value="em_analise">Em Análise</option>
<option value="concluida">Concluída</option>
@@ -234,7 +235,7 @@
<span class="label-text font-semibold">Tipo</span>
</label>
<select bind:value={tipoFiltro} class="select select-bordered">
<option value={null}>Todos</option>
<option value="">Todos</option>
<option value="acesso">Acesso</option>
<option value="correcao">Correção</option>
<option value="exclusao">Exclusão</option>