@@ -1,14 +1,21 @@
|
||||
import { createConvexHttpClient } from '@mmailaender/convex-better-auth-svelte/sveltekit';
|
||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||
import { error, redirect } from '@sveltejs/kit';
|
||||
import type { FunctionReference } from 'convex/server';
|
||||
|
||||
export const load = async ({ locals }) => {
|
||||
export const load = async ({ locals, url }) => {
|
||||
if (!locals.token) {
|
||||
throw redirect(302, '/login?redirect=' + url.pathname);
|
||||
}
|
||||
try {
|
||||
const client = createConvexHttpClient({ token: locals.token });
|
||||
const currentUser = await client.query(api.auth.getCurrentUser, {});
|
||||
const currentUser = await client.query(api.auth.getCurrentUser as FunctionReference<'query'>);
|
||||
|
||||
if (!currentUser) {
|
||||
throw redirect(302, '/login?redirect=' + url.pathname);
|
||||
}
|
||||
return { currentUser };
|
||||
} catch (error) {
|
||||
console.error('Erro ao carregar usuário atual no layout do dashboard:', error);
|
||||
// Evita quebrar toda a área logada em caso de falha transitória na API/auth
|
||||
return { currentUser: null };
|
||||
} catch {
|
||||
return error(500, 'Ops! Ocorreu um erro, tente novamente mais tarde.');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,103 +1,114 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { useQuery } from 'convex-svelte';
|
||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||
import ActionGuard from '$lib/components/ActionGuard.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { Toaster } from 'svelte-sonner';
|
||||
import PushNotificationManager from '$lib/components/PushNotificationManager.svelte';
|
||||
import Footer from '$lib/components/Footer.svelte';
|
||||
import Header from '$lib/components/Header.svelte';
|
||||
import MenuToggleIcon from '$lib/components/MenuToggleIcon.svelte';
|
||||
import Sidebar from '$lib/components/Sidebar.svelte';
|
||||
import DashboardHeaderActions from '$lib/components/dashboard/DashboardHeaderActions.svelte';
|
||||
import ChatWidget from '$lib/components/chat/ChatWidget.svelte';
|
||||
import PresenceManager from '$lib/components/chat/PresenceManager.svelte';
|
||||
|
||||
const { children } = $props();
|
||||
|
||||
// Usuário atual e consentimento LGPD
|
||||
const currentUser = useQuery(api.auth.getCurrentUser, {});
|
||||
const consentimentoLGPD = useQuery(api.lgpd.verificarConsentimento, { tipo: 'termo_uso' });
|
||||
let sidebarOpen = $state(false);
|
||||
let isDesktop = $state(false);
|
||||
const toggleSidebar = () => (sidebarOpen = !sidebarOpen);
|
||||
const closeSidebar = () => (sidebarOpen = false);
|
||||
|
||||
// Redirecionar para o termo de consentimento se obrigatório e não aceito
|
||||
$effect(() => {
|
||||
const p = page.url.pathname;
|
||||
// No desktop, abrir por padrão; no mobile, começar fechado
|
||||
onMount(() => {
|
||||
const mql = window.matchMedia('(min-width: 1024px)');
|
||||
|
||||
// Rotas públicas/que não exigem termo
|
||||
if (
|
||||
p === '/' ||
|
||||
p === '/abrir-chamado' ||
|
||||
p === '/termo-consentimento' ||
|
||||
p.startsWith('/privacidade') ||
|
||||
p.startsWith('/api/')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const update = () => {
|
||||
isDesktop = mql.matches;
|
||||
};
|
||||
|
||||
// Precisa estar autenticado para exigir LGPD
|
||||
if (!currentUser?.data) {
|
||||
return;
|
||||
}
|
||||
update();
|
||||
sidebarOpen = mql.matches;
|
||||
|
||||
// Query ainda carregando ou sem dados
|
||||
if (!consentimentoLGPD?.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = consentimentoLGPD.data;
|
||||
|
||||
if (data.termoObrigatorio && !data.aceito) {
|
||||
const redirect = encodeURIComponent(p);
|
||||
window.location.href = `/termo-consentimento?redirect=${redirect}`;
|
||||
}
|
||||
});
|
||||
|
||||
// Resolver recurso/ação a partir da rota
|
||||
const routeAction = $derived.by(() => {
|
||||
const p = page.url.pathname;
|
||||
if (p === '/' || p === '/abrir-chamado') return null;
|
||||
|
||||
// Funcionários
|
||||
if (p.startsWith('/recursos-humanos/funcionarios')) {
|
||||
if (p.includes('/cadastro')) return { recurso: 'funcionarios', acao: 'criar' };
|
||||
if (p.includes('/excluir')) return { recurso: 'funcionarios', acao: 'excluir' };
|
||||
if (p.includes('/editar') || p.includes('/funcionarioId'))
|
||||
return { recurso: 'funcionarios', acao: 'editar' };
|
||||
return { recurso: 'funcionarios', acao: 'listar' };
|
||||
}
|
||||
|
||||
// Símbolos
|
||||
if (p.startsWith('/recursos-humanos/simbolos')) {
|
||||
if (p.includes('/cadastro')) return { recurso: 'simbolos', acao: 'criar' };
|
||||
if (p.includes('/excluir')) return { recurso: 'simbolos', acao: 'excluir' };
|
||||
if (p.includes('/editar') || p.includes('/simboloId'))
|
||||
return { recurso: 'simbolos', acao: 'editar' };
|
||||
return { recurso: 'simbolos', acao: 'listar' };
|
||||
}
|
||||
|
||||
// Outras áreas (uso genérico: ver)
|
||||
if (p.startsWith('/financeiro')) return { recurso: 'financeiro', acao: 'ver' };
|
||||
if (p.startsWith('/controladoria')) return { recurso: 'controladoria', acao: 'ver' };
|
||||
if (p.startsWith('/licitacoes')) return { recurso: 'licitacoes', acao: 'ver' };
|
||||
if (p.startsWith('/compras')) return { recurso: 'compras', acao: 'ver' };
|
||||
if (p.startsWith('/juridico')) return { recurso: 'juridico', acao: 'ver' };
|
||||
if (p.startsWith('/comunicacao')) return { recurso: 'comunicacao', acao: 'ver' };
|
||||
if (p.startsWith('/programas-esportivos'))
|
||||
return { recurso: 'programas_esportivos', acao: 'ver' };
|
||||
if (p.startsWith('/secretaria-executiva'))
|
||||
return { recurso: 'secretaria_executiva', acao: 'ver' };
|
||||
if (p.startsWith('/gestao-pessoas')) return { recurso: 'gestao_pessoas', acao: 'ver' };
|
||||
|
||||
return null;
|
||||
mql.addEventListener('change', update);
|
||||
return () => mql.removeEventListener('change', update);
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if routeAction}
|
||||
<ActionGuard recurso={routeAction.recurso} acao={routeAction.acao}>
|
||||
<main id="container-central" class="w-full max-w-none px-3 py-4 lg:px-4">
|
||||
{@render children()}
|
||||
</main>
|
||||
</ActionGuard>
|
||||
{:else}
|
||||
<main id="container-central" class="w-full max-w-none px-3 py-4 lg:px-4">
|
||||
{@render children()}
|
||||
</main>
|
||||
{/if}
|
||||
<div
|
||||
class="bg-base-100 text-base-content selection:bg-primary selection:text-primary-content flex min-h-screen flex-col font-sans"
|
||||
>
|
||||
<Header>
|
||||
{#snippet left()}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-sm"
|
||||
aria-label={sidebarOpen ? 'Fechar menu' : 'Abrir menu'}
|
||||
onclick={toggleSidebar}
|
||||
>
|
||||
<MenuToggleIcon open={sidebarOpen} class="h-5 w-5" />
|
||||
</button>
|
||||
{/snippet}
|
||||
|
||||
{#snippet right()}
|
||||
<DashboardHeaderActions />
|
||||
{/snippet}
|
||||
</Header>
|
||||
|
||||
<div class="relative flex min-h-[calc(100vh-4rem)] flex-1">
|
||||
<!-- Overlay (mobile) -->
|
||||
{#if sidebarOpen}
|
||||
<div
|
||||
class="fixed inset-0 z-40 bg-black/30 backdrop-blur-[1px] lg:hidden"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-label="Fechar menu"
|
||||
onclick={closeSidebar}
|
||||
onkeydown={(e) => e.key === 'Escape' && closeSidebar()}
|
||||
></div>
|
||||
{/if}
|
||||
|
||||
<!-- Sidebar -->
|
||||
<aside
|
||||
class="bg-base-100 border-base-200 fixed top-16 bottom-0 left-0 z-50 w-72 border-r shadow-sm transition-transform duration-200"
|
||||
class:translate-x-0={sidebarOpen}
|
||||
class:-translate-x-full={!sidebarOpen}
|
||||
>
|
||||
<div class="h-full overflow-y-auto">
|
||||
<Sidebar onNavigate={() => !isDesktop && closeSidebar()} />
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Conteúdo -->
|
||||
<div
|
||||
class="flex min-w-0 flex-1 flex-col justify-between transition-[padding] duration-200 {sidebarOpen
|
||||
? 'lg:pl-72'
|
||||
: 'lg:pl-0'}"
|
||||
>
|
||||
<div id="container-central" class="w-full max-w-none px-3 py-4 lg:px-4">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Componentes de Chat (gerenciam auth internamente) -->
|
||||
<PresenceManager />
|
||||
<ChatWidget />
|
||||
</div>
|
||||
|
||||
<!-- Toast Notifications (Sonner) -->
|
||||
<Toaster position="top-right" richColors closeButton expand={true} />
|
||||
|
||||
<!-- Push Notification Manager (registra subscription automaticamente) -->
|
||||
<PushNotificationManager />
|
||||
|
||||
<style>
|
||||
/* Evita “corredor” quando páginas usam `container mx-auto` */
|
||||
#container-central :global(.container) {
|
||||
max-width: none !important;
|
||||
}
|
||||
#container-central :global(.container.mx-auto) {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { resolve } from '$app/paths';
|
||||
import { UserPlus, Mail, Clock, Award, TrendingUp, Zap, Users, Database } from 'lucide-svelte';
|
||||
import ProtectedRoute from '$lib/components/ProtectedRoute.svelte';
|
||||
import { loginModalStore } from '$lib/stores/loginModal.svelte';
|
||||
|
||||
// Queries para dados do dashboard
|
||||
const statsQuery = useQuery(api.dashboard.getStats, {});
|
||||
@@ -36,7 +35,12 @@
|
||||
|
||||
// Se for erro de autenticação, abrir modal de login automaticamente
|
||||
if (error === 'auth_required') {
|
||||
loginModalStore.open(route || to.url.pathname);
|
||||
const redirectTo = route || to.url.pathname;
|
||||
goto(`${resolve('/login')}?redirect=${encodeURIComponent(redirectTo)}`, {
|
||||
replaceState: true,
|
||||
noScroll: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Limpar URL usando SvelteKit (após router estar inicializado)
|
||||
@@ -65,7 +69,12 @@
|
||||
const route = urlParams.get('route') || urlParams.get('redirect') || '';
|
||||
|
||||
if (error === 'auth_required') {
|
||||
loginModalStore.open(route || window.location.pathname);
|
||||
const redirectTo = route || window.location.pathname;
|
||||
goto(`${resolve('/login')}?redirect=${encodeURIComponent(redirectTo)}`, {
|
||||
replaceState: true,
|
||||
noScroll: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +140,7 @@
|
||||
</script>
|
||||
|
||||
<ProtectedRoute>
|
||||
<main class="container mx-auto px-4 py-4">
|
||||
<main class="w-full px-4 py-4">
|
||||
<!-- Alerta de Acesso Negado / Autenticação -->
|
||||
{#if showAlert}
|
||||
{@const alertData = getAlertMessage()}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
3
apps/web/src/routes/(dashboard)/compras/+page.server.ts
Normal file
3
apps/web/src/routes/(dashboard)/compras/+page.server.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -2,7 +2,6 @@
|
||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||
import { useConvexClient, useQuery } from 'convex-svelte';
|
||||
import ActionGuard from '$lib/components/ActionGuard.svelte';
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
@@ -232,25 +231,23 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<ActionGuard recurso="setores" acao="criar">
|
||||
<button class="btn btn-primary shadow-lg" onclick={openCreateModal}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Novo Setor
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button class="btn btn-primary shadow-lg" onclick={openCreateModal}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Novo Setor
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -307,52 +304,48 @@
|
||||
<td class="text-base-content/60 text-sm">{formatDate(setor.createdAt)}</td>
|
||||
<td class="text-right">
|
||||
<div class="flex justify-end gap-2">
|
||||
<ActionGuard recurso="setores" acao="editar">
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
onclick={() => openEditModal(setor)}
|
||||
aria-label="Editar setor {setor.nome}"
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
onclick={() => openEditModal(setor)}
|
||||
aria-label="Editar setor {setor.nome}"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<ActionGuard recurso="setores" acao="excluir">
|
||||
<button
|
||||
class="btn btn-ghost btn-sm text-error"
|
||||
onclick={() => openDeleteModal(setor)}
|
||||
aria-label="Excluir setor {setor.nome}"
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-ghost btn-sm text-error"
|
||||
onclick={() => openDeleteModal(setor)}
|
||||
aria-label="Excluir setor {setor.nome}"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -1,368 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||
import { useConvexClient } from 'convex-svelte';
|
||||
|
||||
import { resolve } from '$app/paths';
|
||||
|
||||
const convex = useConvexClient();
|
||||
|
||||
let matricula = $state('');
|
||||
let email = $state('');
|
||||
let carregando = $state(false);
|
||||
let notice = $state<{
|
||||
type: 'success' | 'error' | 'info';
|
||||
message: string;
|
||||
} | null>(null);
|
||||
let solicitacaoEnviada = $state(false);
|
||||
|
||||
async function handleSubmit(e: Event) {
|
||||
e.preventDefault();
|
||||
notice = null;
|
||||
|
||||
if (!matricula || !email) {
|
||||
notice = {
|
||||
type: 'error',
|
||||
message: 'Por favor, preencha todos os campos'
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
carregando = true;
|
||||
|
||||
try {
|
||||
// Verificar se o usuário existe
|
||||
const usuarios = await convex.query(api.usuarios.listar, {
|
||||
matricula: matricula
|
||||
});
|
||||
|
||||
const usuario = usuarios.find((u) => u.matricula === matricula && u.email === email);
|
||||
|
||||
if (!usuario) {
|
||||
notice = {
|
||||
type: 'error',
|
||||
message: 'Matrícula ou e-mail não encontrados. Verifique os dados e tente novamente.'
|
||||
};
|
||||
carregando = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Simular envio de solicitação
|
||||
solicitacaoEnviada = true;
|
||||
notice = {
|
||||
type: 'success',
|
||||
message: 'Solicitação enviada com sucesso! A equipe de TI entrará em contato em breve.'
|
||||
};
|
||||
|
||||
// Limpar campos
|
||||
matricula = '';
|
||||
email = '';
|
||||
} catch (error: any) {
|
||||
notice = {
|
||||
type: 'error',
|
||||
message: error.message || 'Erro ao processar solicitação'
|
||||
};
|
||||
} finally {
|
||||
carregando = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<main class="container mx-auto max-w-2xl px-4 py-8">
|
||||
<!-- Header -->
|
||||
<div class="mb-8">
|
||||
<div class="mb-2 flex items-center gap-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-primary h-10 w-10"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<h1 class="text-primary text-4xl font-bold">Esqueci Minha Senha</h1>
|
||||
</div>
|
||||
<p class="text-base-content/70 text-lg">Solicite a recuperação da sua senha de acesso</p>
|
||||
</div>
|
||||
|
||||
<!-- Breadcrumbs -->
|
||||
<div class="breadcrumbs mb-6 text-sm">
|
||||
<ul>
|
||||
<li><a href={resolve('/')}>Dashboard</a></li>
|
||||
<li>Esqueci Minha Senha</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Alertas -->
|
||||
{#if notice}
|
||||
<div
|
||||
class="alert {notice.type === 'success'
|
||||
? 'alert-success'
|
||||
: notice.type === 'error'
|
||||
? 'alert-error'
|
||||
: 'alert-info'} mb-6 shadow-lg"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
{#if notice.type === 'success'}
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
{:else if notice.type === 'error'}
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
{:else}
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
{/if}
|
||||
</svg>
|
||||
<span>{notice.message}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !solicitacaoEnviada}
|
||||
<!-- Formulário -->
|
||||
<div class="card bg-base-100 border-base-300 border shadow-xl">
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info mb-6">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<div>
|
||||
<h3 class="font-bold">Como funciona?</h3>
|
||||
<p class="text-sm">
|
||||
Informe sua matrícula e e-mail cadastrados. A equipe de TI receberá sua solicitação e
|
||||
entrará em contato para resetar sua senha.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onsubmit={handleSubmit} class="space-y-6">
|
||||
<!-- Matrícula -->
|
||||
<div class="form-control">
|
||||
<label class="label" for="matricula">
|
||||
<span class="label-text font-semibold">Matrícula</span>
|
||||
<span class="label-text-alt text-error">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="matricula"
|
||||
type="text"
|
||||
placeholder="Digite sua matrícula"
|
||||
class="input input-bordered input-primary w-full"
|
||||
bind:value={matricula}
|
||||
required
|
||||
disabled={carregando}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- E-mail -->
|
||||
<div class="form-control">
|
||||
<label class="label" for="email">
|
||||
<span class="label-text font-semibold">E-mail</span>
|
||||
<span class="label-text-alt text-error">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="Digite seu e-mail cadastrado"
|
||||
class="input input-bordered input-primary w-full"
|
||||
bind:value={email}
|
||||
required
|
||||
disabled={carregando}
|
||||
/>
|
||||
<label class="label">
|
||||
<span class="label-text-alt text-base-content/60">
|
||||
Use o e-mail cadastrado no sistema
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Botões -->
|
||||
<div class="mt-8 flex justify-end gap-4">
|
||||
<a href={resolve('/')} class="btn" class:btn-disabled={carregando}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 19l-7-7m0 0l7-7m-7 7h18"
|
||||
/>
|
||||
</svg>
|
||||
Voltar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary" disabled={carregando}>
|
||||
{#if carregando}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
Enviando...
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
Enviar Solicitação
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Mensagem de Sucesso -->
|
||||
<div class="card bg-success/10 border-success/30 border shadow-xl">
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-4 flex justify-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-success h-24 w-24"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 class="text-success mb-4 text-2xl font-bold">Solicitação Enviada!</h2>
|
||||
<p class="text-base-content/70 mb-6">
|
||||
Sua solicitação de recuperação de senha foi enviada para a equipe de TI. Você receberá um
|
||||
contato em breve com as instruções para resetar sua senha.
|
||||
</p>
|
||||
<div class="flex justify-center gap-4">
|
||||
<a href={resolve('/')} class="btn btn-primary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
|
||||
/>
|
||||
</svg>
|
||||
Voltar ao Dashboard
|
||||
</a>
|
||||
<button type="button" class="btn" onclick={() => (solicitacaoEnviada = false)}>
|
||||
Enviar Nova Solicitação
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Card de Contato -->
|
||||
<div class="card bg-base-200 mt-6 shadow-lg">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-lg">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-info h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
Precisa de Ajuda?
|
||||
</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Se você não conseguir recuperar sua senha ou tiver problemas com o sistema, entre em contato
|
||||
diretamente com a equipe de TI:
|
||||
</p>
|
||||
<div class="mt-4 space-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-primary h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-sm">ti@sgse.pe.gov.br</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-primary h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-sm">(81) 3183-8000</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
3
apps/web/src/routes/(dashboard)/fluxos/+page.server.ts
Normal file
3
apps/web/src/routes/(dashboard)/fluxos/+page.server.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -3,7 +3,6 @@
|
||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||
import { useConvexClient, useQuery } from 'convex-svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import ActionGuard from '$lib/components/ActionGuard.svelte';
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
@@ -164,26 +163,24 @@
|
||||
<option value="archived">Arquivado</option>
|
||||
</select>
|
||||
|
||||
<ActionGuard recurso="fluxos_templates" acao="criar">
|
||||
<button class="btn btn-secondary shadow-lg" onclick={openCreateModal}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Novo Template
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button class="btn btn-secondary shadow-lg" onclick={openCreateModal}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Novo Template
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -4,7 +4,6 @@
|
||||
import { useConvexClient, useQuery } from 'convex-svelte';
|
||||
import { resolve } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import ActionGuard from '$lib/components/ActionGuard.svelte';
|
||||
|
||||
const client = useConvexClient();
|
||||
let instanceId = $derived($page.params.id as Id<'flowInstances'>);
|
||||
@@ -416,26 +415,24 @@
|
||||
</div>
|
||||
|
||||
{#if instance.status === 'active'}
|
||||
<ActionGuard recurso="fluxos_instancias" acao="cancelar">
|
||||
<button class="btn btn-error btn-outline" onclick={() => (showCancelModal = true)}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
Cancelar Fluxo
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button class="btn btn-error btn-outline" onclick={() => (showCancelModal = true)}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
Cancelar Fluxo
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -616,67 +613,59 @@
|
||||
{#if instance.status === 'active'}
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#if step.status === 'pending'}
|
||||
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
|
||||
<button
|
||||
class="btn btn-info btn-sm"
|
||||
onclick={() => handleStartStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Iniciar
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button
|
||||
class="btn btn-info btn-sm"
|
||||
onclick={() => handleStartStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Iniciar
|
||||
</button>
|
||||
{:else if step.status === 'in_progress'}
|
||||
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
|
||||
<button
|
||||
class="btn btn-success btn-sm"
|
||||
onclick={() => handleCompleteStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Concluir
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-warning btn-sm"
|
||||
onclick={() => handleBlockStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Bloquear
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button
|
||||
class="btn btn-success btn-sm"
|
||||
onclick={() => handleCompleteStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Concluir
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-warning btn-sm"
|
||||
onclick={() => handleBlockStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Bloquear
|
||||
</button>
|
||||
{:else if step.status === 'blocked'}
|
||||
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
|
||||
<button
|
||||
class="btn btn-info btn-sm"
|
||||
onclick={() => handleStartStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Desbloquear
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button
|
||||
class="btn btn-info btn-sm"
|
||||
onclick={() => handleStartStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Desbloquear
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<ActionGuard recurso="fluxos_instancias" acao="atribuir_usuario">
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
onclick={() => openReassignModal(step)}
|
||||
aria-label="Reatribuir responsável"
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
onclick={() => openReassignModal(step)}
|
||||
aria-label="Reatribuir responsável"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
@@ -700,29 +689,27 @@
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<ActionGuard recurso="fluxos_documentos" acao="upload">
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
onclick={() => openUploadModal(step)}
|
||||
aria-label="Upload de documento"
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
onclick={() => openUploadModal(step)}
|
||||
aria-label="Upload de documento"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -759,15 +746,13 @@
|
||||
/>
|
||||
</svg>
|
||||
{doc.name}
|
||||
<ActionGuard recurso="fluxos_documentos" acao="excluir">
|
||||
<button
|
||||
class="btn btn-ghost btn-xs text-error"
|
||||
onclick={() => handleDeleteDocument(doc._id)}
|
||||
aria-label="Excluir documento {doc.name}"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button
|
||||
class="btn btn-ghost btn-xs text-error"
|
||||
onclick={() => handleDeleteDocument(doc._id)}
|
||||
aria-label="Excluir documento {doc.name}"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -3,7 +3,6 @@
|
||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||
import { useConvexClient, useQuery } from 'convex-svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import ActionGuard from '$lib/components/ActionGuard.svelte';
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
@@ -148,26 +147,24 @@
|
||||
<option value="cancelled">Cancelado</option>
|
||||
</select>
|
||||
|
||||
<ActionGuard recurso="fluxos_instancias" acao="criar">
|
||||
<button class="btn btn-info shadow-lg" onclick={openCreateModal}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Nova Instância
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button class="btn btn-info shadow-lg" onclick={openCreateModal}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Nova Instância
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
3
apps/web/src/routes/(dashboard)/juridico/+page.server.ts
Normal file
3
apps/web/src/routes/(dashboard)/juridico/+page.server.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -3,7 +3,6 @@
|
||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||
import { useConvexClient, useQuery } from 'convex-svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import ActionGuard from '$lib/components/ActionGuard.svelte';
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
@@ -148,26 +147,24 @@
|
||||
<option value="cancelled">Cancelado</option>
|
||||
</select>
|
||||
|
||||
<ActionGuard recurso="fluxos_instancias" acao="criar">
|
||||
<button class="btn btn-info shadow-lg" onclick={openCreateModal}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Novo Fluxo
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button class="btn btn-info shadow-lg" onclick={openCreateModal}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Novo Fluxo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -5,7 +5,6 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import ActionGuard from '$lib/components/ActionGuard.svelte';
|
||||
import RelogioPrazo from '$lib/components/RelogioPrazo.svelte';
|
||||
|
||||
const client = useConvexClient();
|
||||
@@ -651,26 +650,24 @@
|
||||
</div>
|
||||
|
||||
{#if instance.status === 'active'}
|
||||
<ActionGuard recurso="fluxos_instancias" acao="cancelar">
|
||||
<button class="btn btn-error btn-outline" onclick={() => (showCancelModal = true)}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
Cancelar Fluxo
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button class="btn btn-error btn-outline" onclick={() => (showCancelModal = true)}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
Cancelar Fluxo
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -867,68 +864,60 @@
|
||||
{#if instance.status === 'active'}
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#if step.status === 'pending'}
|
||||
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
|
||||
<button
|
||||
class="btn btn-info btn-sm"
|
||||
onclick={() => handleStartStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Iniciar
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button
|
||||
class="btn btn-info btn-sm"
|
||||
onclick={() => handleStartStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Iniciar
|
||||
</button>
|
||||
{:else if step.status === 'in_progress'}
|
||||
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
|
||||
<button
|
||||
class="btn btn-success btn-sm"
|
||||
onclick={() => handleCompleteStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Concluir
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-warning btn-sm"
|
||||
onclick={() => handleBlockStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Bloquear
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button
|
||||
class="btn btn-success btn-sm"
|
||||
onclick={() => handleCompleteStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Concluir
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-warning btn-sm"
|
||||
onclick={() => handleBlockStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Bloquear
|
||||
</button>
|
||||
{:else if step.status === 'blocked'}
|
||||
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
|
||||
<button
|
||||
class="btn btn-info btn-sm"
|
||||
onclick={() => handleStartStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Desbloquear
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<button
|
||||
class="btn btn-info btn-sm"
|
||||
onclick={() => handleStartStep(step._id)}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Desbloquear
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<ActionGuard recurso="fluxos_instancias" acao="atribuir_usuario">
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
onclick={() => openReassignModal(step)}
|
||||
aria-label="Reatribuir responsável"
|
||||
title="Reatribuir responsável"
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
onclick={() => openReassignModal(step)}
|
||||
aria-label="Reatribuir responsável"
|
||||
title="Reatribuir responsável"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1104,30 +1093,28 @@
|
||||
{/if}
|
||||
</div>
|
||||
{#if instance.status === 'active'}
|
||||
<ActionGuard recurso="fluxos_documentos" acao="upload">
|
||||
<button
|
||||
class="btn btn-ghost btn-xs"
|
||||
onclick={() => openUploadModal(step)}
|
||||
aria-label="Upload de documento"
|
||||
<button
|
||||
class="btn btn-ghost btn-xs"
|
||||
onclick={() => openUploadModal(step)}
|
||||
aria-label="Upload de documento"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
||||
/>
|
||||
</svg>
|
||||
Enviar
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
||||
/>
|
||||
</svg>
|
||||
Enviar
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1158,29 +1145,27 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if instance.status === 'active'}
|
||||
<ActionGuard recurso="fluxos_documentos" acao="excluir">
|
||||
<button
|
||||
class="btn btn-ghost btn-xs text-error shrink-0"
|
||||
onclick={() => handleDeleteDocument(doc._id)}
|
||||
aria-label="Excluir documento {doc.name}"
|
||||
<button
|
||||
class="btn btn-ghost btn-xs text-error shrink-0"
|
||||
onclick={() => handleDeleteDocument(doc._id)}
|
||||
aria-label="Excluir documento {doc.name}"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</ActionGuard>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
3
apps/web/src/routes/(dashboard)/pedidos/+page.server.ts
Normal file
3
apps/web/src/routes/(dashboard)/pedidos/+page.server.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
3
apps/web/src/routes/(dashboard)/perfil/+page.server.ts
Normal file
3
apps/web/src/routes/(dashboard)/perfil/+page.server.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -1,434 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { Shield, FileText, Mail, Phone, Calendar } from 'lucide-svelte';
|
||||
import { useQuery } from 'convex-svelte';
|
||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||
|
||||
const configLGPD = useQuery(api.lgpd.obterConfiguracaoLGPD, {});
|
||||
|
||||
const encarregadoNome = $derived(
|
||||
configLGPD?.data?.encarregadoNome || 'Encarregado de Proteção de Dados'
|
||||
);
|
||||
const encarregadoEmail = $derived(
|
||||
configLGPD?.data?.encarregadoEmail || 'lgpd@esportes.pe.gov.br'
|
||||
);
|
||||
const encarregadoTelefone = $derived(configLGPD?.data?.encarregadoTelefone || '(81) 3184-XXXX');
|
||||
const encarregadoHorario = $derived(
|
||||
configLGPD?.data?.encarregadoHorarioAtendimento || 'Segunda a Sexta, das 8h às 17h'
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto max-w-4xl px-4 py-8">
|
||||
<!-- Header -->
|
||||
<div class="mb-8">
|
||||
<div class="mb-4 flex items-center gap-4">
|
||||
<div class="bg-primary/10 rounded-xl p-3">
|
||||
<Shield class="text-primary h-8 w-8" strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-base-content text-3xl font-bold">Política de Privacidade</h1>
|
||||
<p class="text-base-content/60 mt-1">
|
||||
Lei Geral de Proteção de Dados Pessoais (LGPD) - Lei nº 13.709/2018
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-base-content/60 flex items-center gap-2 text-sm">
|
||||
<Calendar class="h-4 w-4" />
|
||||
<span>Última atualização: {new Date().toLocaleDateString('pt-BR')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Conteúdo -->
|
||||
<div class="prose prose-lg max-w-none">
|
||||
<!-- Introdução -->
|
||||
<section class="card bg-base-100 mb-6 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4 text-2xl">1. Introdução</h2>
|
||||
<p class="text-base-content/80">
|
||||
A Secretaria de Esportes do Estado de Pernambuco, no exercício de suas atribuições
|
||||
constitucionais e legais, está comprometida com a proteção dos dados pessoais de seus
|
||||
servidores, colaboradores e cidadãos, em conformidade com a Lei Geral de Proteção de Dados
|
||||
Pessoais (LGPD) - Lei nº 13.709/2018.
|
||||
</p>
|
||||
<p class="text-base-content/80 mt-4">
|
||||
Esta Política de Privacidade descreve como coletamos, utilizamos, armazenamos e protegemos
|
||||
seus dados pessoais no Sistema de Gestão da Secretaria de Esportes (SGSE).
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Dados Coletados -->
|
||||
<section class="card bg-base-100 mb-6 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4 text-2xl">2. Dados Pessoais Coletados</h2>
|
||||
<p class="text-base-content/80 mb-4">
|
||||
O SGSE coleta e processa os seguintes tipos de dados pessoais:
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-start gap-3">
|
||||
<FileText class="text-primary mt-1 h-5 w-5 flex-shrink-0" />
|
||||
<div>
|
||||
<h3 class="font-semibold">Dados de Identificação</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Nome completo, CPF, RG, data de nascimento, naturalidade, nacionalidade, estado
|
||||
civil, filiação (nome do pai e mãe)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<FileText class="text-primary mt-1 h-5 w-5 flex-shrink-0" />
|
||||
<div>
|
||||
<h3 class="font-semibold">Dados de Contato</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
E-mail, telefone, endereço residencial e endereços de marcação de ponto
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<FileText class="text-primary mt-1 h-5 w-5 flex-shrink-0" />
|
||||
<div>
|
||||
<h3 class="font-semibold">Dados Profissionais</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Matrícula, cargo, função, setor, data de admissão, regime de trabalho, documentos
|
||||
profissionais (CTPS, título eleitor, reservista, PIS)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<FileText class="text-primary mt-1 h-5 w-5 flex-shrink-0" />
|
||||
<div>
|
||||
<h3 class="font-semibold">Dados de Saúde</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Atestados médicos, licenças de saúde, grupo sanguíneo, fator RH (quando necessário
|
||||
para atividades específicas)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<FileText class="text-primary mt-1 h-5 w-5 flex-shrink-0" />
|
||||
<div>
|
||||
<h3 class="font-semibold">Dados de Acesso</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Credenciais de acesso, logs de acesso, endereço IP, histórico de atividades no
|
||||
sistema
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Finalidade -->
|
||||
<section class="card bg-base-100 mb-6 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4 text-2xl">3. Finalidade do Tratamento</h2>
|
||||
<p class="text-base-content/80 mb-4">
|
||||
Os dados pessoais são tratados para as seguintes finalidades:
|
||||
</p>
|
||||
<ul class="text-base-content/80 list-inside list-disc space-y-2">
|
||||
<li>Gestão de recursos humanos e folha de pagamento</li>
|
||||
<li>Controle de ponto e registro de jornada de trabalho</li>
|
||||
<li>Gestão de férias, ausências e licenças</li>
|
||||
<li>Processamento de atestados médicos e licenças de saúde</li>
|
||||
<li>Gestão de chamados e suporte técnico</li>
|
||||
<li>Comunicação interna e notificações</li>
|
||||
<li>Cumprimento de obrigações legais e regulatórias</li>
|
||||
<li>Execução de políticas públicas</li>
|
||||
<li>Segurança e prevenção de fraudes</li>
|
||||
<li>Auditoria e controle interno</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Base Legal -->
|
||||
<section class="card bg-base-100 mb-6 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4 text-2xl">4. Base Legal do Tratamento</h2>
|
||||
<p class="text-base-content/80 mb-4">
|
||||
O tratamento de dados pessoais no SGSE fundamenta-se nas seguintes bases legais, conforme
|
||||
previsto no Art. 7º da LGPD:
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<div class="bg-primary/5 rounded-lg p-4">
|
||||
<h3 class="text-primary mb-2 font-semibold">I. Execução de Políticas Públicas</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Art. 7º, II - Para a execução de políticas públicas previstas em leis ou regulamentos
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-primary/5 rounded-lg p-4">
|
||||
<h3 class="text-primary mb-2 font-semibold">II. Cumprimento de Obrigação Legal</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Art. 7º, I - Para cumprimento de obrigação legal ou regulatória pelo controlador
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-primary/5 rounded-lg p-4">
|
||||
<h3 class="text-primary mb-2 font-semibold">III. Execução de Contrato</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Art. 7º, V - Para a execução de contrato ou de procedimentos preliminares relacionados
|
||||
a contrato do qual seja parte o titular
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-primary/5 rounded-lg p-4">
|
||||
<h3 class="text-primary mb-2 font-semibold">IV. Proteção da Vida e Saúde</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Art. 7º, VI e VII - Para a proteção da vida ou da incolumidade física do titular ou de
|
||||
terceiro, e para a tutela da saúde
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Compartilhamento -->
|
||||
<section class="card bg-base-100 mb-6 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4 text-2xl">5. Compartilhamento de Dados</h2>
|
||||
<p class="text-base-content/80 mb-4">Os dados pessoais podem ser compartilhados com:</p>
|
||||
<ul class="text-base-content/80 list-inside list-disc space-y-2">
|
||||
<li>
|
||||
<strong>Órgãos Públicos:</strong> Quando necessário para cumprimento de obrigações legais
|
||||
ou execução de políticas públicas
|
||||
</li>
|
||||
<li>
|
||||
<strong>Fornecedores de Serviços:</strong> Empresas contratadas para prestação de serviços
|
||||
técnicos, sempre com garantias de proteção de dados
|
||||
</li>
|
||||
<li>
|
||||
<strong>Autoridades Competentes:</strong> Quando exigido por determinação judicial ou legal
|
||||
</li>
|
||||
</ul>
|
||||
<div class="alert alert-info mt-4">
|
||||
<p class="text-sm">
|
||||
Todos os compartilhamentos são realizados com base legal e com garantias de proteção dos
|
||||
dados pessoais.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Segurança -->
|
||||
<section class="card bg-base-100 mb-6 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4 text-2xl">6. Medidas de Segurança</h2>
|
||||
<p class="text-base-content/80 mb-4">
|
||||
Adotamos medidas técnicas e administrativas para proteger seus dados pessoais:
|
||||
</p>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="bg-base-200 rounded-lg p-4">
|
||||
<h3 class="mb-2 font-semibold">Medidas Técnicas</h3>
|
||||
<ul class="text-base-content/70 space-y-1 text-sm">
|
||||
<li>• Criptografia de dados sensíveis</li>
|
||||
<li>• Controle de acesso por permissões</li>
|
||||
<li>• Logs de auditoria</li>
|
||||
<li>• Backup regular</li>
|
||||
<li>• Monitoramento de segurança</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-base-200 rounded-lg p-4">
|
||||
<h3 class="mb-2 font-semibold">Medidas Administrativas</h3>
|
||||
<ul class="text-base-content/70 space-y-1 text-sm">
|
||||
<li>• Treinamento de equipe</li>
|
||||
<li>• Políticas de acesso</li>
|
||||
<li>• Procedimentos de segurança</li>
|
||||
<li>• Gestão de incidentes</li>
|
||||
<li>• Revisão periódica</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Retenção -->
|
||||
<section class="card bg-base-100 mb-6 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4 text-2xl">7. Prazo de Retenção</h2>
|
||||
<p class="text-base-content/80 mb-4">
|
||||
Os dados pessoais são mantidos pelo prazo necessário para:
|
||||
</p>
|
||||
<ul class="text-base-content/80 list-inside list-disc space-y-2">
|
||||
<li>
|
||||
<strong>Dados de Funcionários Ativos:</strong> Durante todo o período de vínculo empregatício/estatutário
|
||||
</li>
|
||||
<li>
|
||||
<strong>Dados de Funcionários Inativos:</strong> Conforme prazo legal aplicável (em geral,
|
||||
5 anos após desligamento)
|
||||
</li>
|
||||
<li>
|
||||
<strong>Logs de Acesso:</strong> 2 anos, conforme recomendação da ANPD
|
||||
</li>
|
||||
<li>
|
||||
<strong>Documentos Trabalhistas:</strong> Conforme legislação trabalhista aplicável
|
||||
</li>
|
||||
</ul>
|
||||
<p class="text-base-content/80 mt-4">
|
||||
Após o término do prazo de retenção, os dados são eliminados de forma segura, exceto
|
||||
quando houver obrigação legal de manutenção.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Direitos do Titular -->
|
||||
<section class="card bg-base-100 mb-6 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4 text-2xl">8. Direitos do Titular dos Dados</h2>
|
||||
<p class="text-base-content/80 mb-4">
|
||||
Conforme previsto no Art. 18 da LGPD, você possui os seguintes direitos:
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="badge badge-primary badge-lg">1</div>
|
||||
<div>
|
||||
<h3 class="font-semibold">Confirmação da Existência de Tratamento</h3>
|
||||
<p class="text-base-content/70 text-sm">Confirmar se tratamos seus dados pessoais</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="badge badge-primary badge-lg">2</div>
|
||||
<div>
|
||||
<h3 class="font-semibold">Acesso aos Dados</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Acessar seus dados pessoais tratados por nós
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="badge badge-primary badge-lg">3</div>
|
||||
<div>
|
||||
<h3 class="font-semibold">Correção de Dados</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Solicitar correção de dados incompletos, inexatos ou desatualizados
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="badge badge-primary badge-lg">4</div>
|
||||
<div>
|
||||
<h3 class="font-semibold">Anonimização, Bloqueio ou Eliminação</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Solicitar anonimização, bloqueio ou eliminação de dados desnecessários ou excessivos
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="badge badge-primary badge-lg">5</div>
|
||||
<div>
|
||||
<h3 class="font-semibold">Portabilidade dos Dados</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Solicitar a portabilidade dos dados a outro fornecedor de serviço ou produto
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="badge badge-primary badge-lg">6</div>
|
||||
<div>
|
||||
<h3 class="font-semibold">Eliminação de Dados</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Solicitar a eliminação dos dados pessoais tratados com base em consentimento
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="badge badge-primary badge-lg">7</div>
|
||||
<div>
|
||||
<h3 class="font-semibold">Informação sobre Compartilhamento</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Obter informações sobre compartilhamento de dados com terceiros
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="badge badge-primary badge-lg">8</div>
|
||||
<div>
|
||||
<h3 class="font-semibold">Revogação de Consentimento</h3>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Revogar seu consentimento, quando aplicável
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<a
|
||||
href={resolve('/privacidade/meus-dados')}
|
||||
class="btn btn-primary btn-lg w-full md:w-auto"
|
||||
>
|
||||
<FileText class="h-5 w-5" />
|
||||
Solicitar Meus Direitos
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Encarregado -->
|
||||
<section class="card bg-base-100 mb-6 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4 text-2xl">9. Encarregado de Proteção de Dados (DPO)</h2>
|
||||
<p class="text-base-content/80 mb-4">
|
||||
Para exercer seus direitos ou esclarecer dúvidas sobre o tratamento de dados pessoais,
|
||||
entre em contato com nosso Encarregado de Proteção de Dados:
|
||||
</p>
|
||||
<div class="bg-primary/5 space-y-3 rounded-lg p-6">
|
||||
{#if encarregadoNome && encarregadoNome !== 'Encarregado de Proteção de Dados'}
|
||||
<div class="flex items-center gap-3">
|
||||
<FileText class="text-primary h-5 w-5" />
|
||||
<div>
|
||||
<p class="text-sm font-semibold">Nome</p>
|
||||
<p class="text-base-content/70">{encarregadoNome}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex items-center gap-3">
|
||||
<Mail class="text-primary h-5 w-5" />
|
||||
<div>
|
||||
<p class="text-sm font-semibold">E-mail</p>
|
||||
<p class="text-base-content/70">{encarregadoEmail}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<Phone class="text-primary h-5 w-5" />
|
||||
<div>
|
||||
<p class="text-sm font-semibold">Telefone</p>
|
||||
<p class="text-base-content/70">{encarregadoTelefone}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<FileText class="text-primary h-5 w-5" />
|
||||
<div>
|
||||
<p class="text-sm font-semibold">Horário de Atendimento</p>
|
||||
<p class="text-base-content/70">{encarregadoHorario}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-info mt-4">
|
||||
<p class="text-sm">
|
||||
As solicitações serão respondidas em até {configLGPD?.data?.prazoRespostaPadrao || 15} (quinze)
|
||||
dias, conforme previsto na LGPD.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Alterações -->
|
||||
<section class="card bg-base-100 mb-6 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4 text-2xl">10. Alterações nesta Política</h2>
|
||||
<p class="text-base-content/80">
|
||||
Esta Política de Privacidade pode ser atualizada periodicamente. Recomendamos que você
|
||||
revise esta página regularmente para estar ciente de quaisquer alterações. A data da
|
||||
última atualização está indicada no topo desta página.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Ações -->
|
||||
<div class="mt-8 flex flex-col gap-4 sm:flex-row">
|
||||
<a href={resolve('/privacidade/meus-dados')} class="btn btn-primary btn-lg flex-1">
|
||||
<FileText class="h-5 w-5" />
|
||||
Solicitar Meus Direitos LGPD
|
||||
</a>
|
||||
<a href={resolve('/termo-consentimento')} class="btn btn-outline btn-lg flex-1">
|
||||
<Shield class="h-5 w-5" />
|
||||
Ver Termo de Consentimento
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export const load = async ({ parent }) => {
|
||||
const { currentUser } = await parent();
|
||||
console.log(currentUser);
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const load = async ({ parent }) => {
|
||||
await parent();
|
||||
};
|
||||
Reference in New Issue
Block a user