diff --git a/apps/web/src/lib/components/ProtectedRoute.svelte b/apps/web/src/lib/components/ProtectedRoute.svelte index 1b7a77d..a131ebc 100644 --- a/apps/web/src/lib/components/ProtectedRoute.svelte +++ b/apps/web/src/lib/components/ProtectedRoute.svelte @@ -2,19 +2,16 @@ import { api } from '@sgse-app/backend/convex/_generated/api'; import { useQuery } from 'convex-svelte'; import type { Snippet } from 'svelte'; - import { onMount } from 'svelte'; const { children, requireAuth = true, allowedRoles = [], - maxLevel = 3, redirectTo = '/' }: { children: Snippet; requireAuth?: boolean; allowedRoles?: string[]; - maxLevel?: number; redirectTo?: string; } = $props(); @@ -72,13 +69,6 @@ } } - // Verificar nível - if (currentUser.data.role?.nivel && currentUser.data.role.nivel > maxLevel) { - const currentPath = window.location.pathname; - window.location.href = `${redirectTo}?error=access_denied&route=${encodeURIComponent(currentPath)}`; - return; - } - // Se chegou aqui, permitir acesso hasAccess = true; isChecking = false; diff --git a/apps/web/src/lib/stores/auth.svelte.ts b/apps/web/src/lib/stores/auth.svelte.ts index c8f5d4c..c28e827 100644 --- a/apps/web/src/lib/stores/auth.svelte.ts +++ b/apps/web/src/lib/stores/auth.svelte.ts @@ -1,6 +1,6 @@ -import type { Id } from '@sgse-app/backend/convex/betterAuth/_generated/dataModel'; import { browser } from '$app/environment'; import { goto } from '$app/navigation'; +import type { Id } from '@sgse-app/backend/convex/_generated/dataModel'; interface Usuario { _id: string; @@ -11,8 +11,7 @@ interface Usuario { role: { _id: string; nome: string; - nivel: number; - setor?: string; + admin?: boolean; }; primeiroAcesso: boolean; avatar?: string; @@ -56,7 +55,7 @@ class AuthStore { } get isAdmin() { - return this.state.usuario?.role.nivel === 0; + return this.state.usuario?.role.admin === true; } get isTI() { diff --git a/apps/web/src/routes/(dashboard)/ti/painel-permissoes/+page.svelte b/apps/web/src/routes/(dashboard)/ti/painel-permissoes/+page.svelte index 3c0c086..0f2286e 100644 --- a/apps/web/src/routes/(dashboard)/ti/painel-permissoes/+page.svelte +++ b/apps/web/src/routes/(dashboard)/ti/painel-permissoes/+page.svelte @@ -19,11 +19,16 @@ let modalNovoPerfilAberto = $state(false); let nomeNovoPerfil = $state(''); let descricaoNovoPerfil = $state(''); - let setorNovoPerfil = $state(''); - let nivelNovoPerfil = $state(3); let roleParaDuplicar = $state | ''>(''); let criandoNovoPerfil = $state(false); + // Estado para modal de edição + let modalEditarPerfilAberto = $state(false); + let roleParaEditar = $state | null>(null); + let descricaoEditarPerfil = $state(''); + let editandoPerfil = $state(false); + let excluindoPerfil = $state(false); + // Controla quais recursos estão expandidos (mostrando as ações) por perfil // Formato: { "roleId-recurso": true/false } let recursosExpandidos: Record = $state({}); @@ -74,13 +79,81 @@ $effect(() => { if (rolesFiltradas && catalogoQuery.data) { for (const roleRow of rolesFiltradas) { - if (roleRow.nivel > 1) { + if (roleRow.admin !== true) { carregarPermissoesRole(roleRow._id); } } } }); + function abrirModalEditar(role: { _id: Id<'roles'>; nome: string; descricao: string }) { + roleParaEditar = role._id; + descricaoEditarPerfil = role.descricao; + modalEditarPerfilAberto = true; + } + + function fecharModalEditar() { + modalEditarPerfilAberto = false; + roleParaEditar = null; + } + + async function editarPerfil() { + if (!roleParaEditar) return; + + editandoPerfil = true; + try { + const resultado = await client.mutation(api.roles.editar, { + roleId: roleParaEditar, + descricao: descricaoEditarPerfil.trim() + }); + + if (resultado.sucesso) { + mostrarMensagem('success', 'Perfil atualizado com sucesso!'); + fecharModalEditar(); + } else { + const mapaErros: Record = { + nome_ja_utilizado: 'Já existe um perfil com este identificador.', + nome_invalido: 'Informe um nome válido com pelo menos 3 caracteres.', + sem_permissao: 'Você não possui permissão para editar perfis.', + role_nao_encontrada: 'Perfil não encontrado.' + }; + mostrarMensagem('error', mapaErros[resultado.erro] ?? resultado.erro); + } + } catch (error: unknown) { + const mensagemErro = error instanceof Error ? error.message : 'Erro ao editar perfil.'; + mostrarMensagem('error', mensagemErro); + } finally { + editandoPerfil = false; + } + } + + async function excluirPerfil(roleId: Id<'roles'>) { + if (!confirm('Tem certeza que deseja excluir este perfil? Esta ação não pode ser desfeita.')) { + return; + } + + excluindoPerfil = true; + try { + const resultado = await client.mutation(api.roles.excluir, { roleId }); + + if (resultado.sucesso) { + mostrarMensagem('success', 'Perfil excluído com sucesso!'); + } else { + const mapaErros: Record = { + sem_permissao: 'Você não possui permissão para excluir perfis.', + role_nao_encontrada: 'Perfil não encontrado.', + role_possui_usuarios: 'Não é possível excluir um perfil que possui usuários vinculados.' + }; + mostrarMensagem('error', mapaErros[resultado.erro] ?? resultado.erro); + } + } catch (error: unknown) { + const mensagemErro = error instanceof Error ? error.message : 'Erro ao excluir perfil.'; + mostrarMensagem('error', mensagemErro); + } finally { + excluindoPerfil = false; + } + } + async function toggleAcao(roleId: Id<'roles'>, recurso: string, acao: string, conceder: boolean) { try { salvando = true; @@ -130,9 +203,7 @@ let podeSalvarNovoPerfil = $derived.by(() => { const nome = nomeNovoPerfil.trim(); - const nivel = Number(nivelNovoPerfil); - const nivelValido = Number.isFinite(nivel) && nivel >= 0 && nivel <= 10; - return nome.length >= 3 && nivelValido && !criandoNovoPerfil; + return nome.length >= 3 && !criandoNovoPerfil; }); let roleDuplicacaoSelecionada = $derived.by(() => { @@ -158,8 +229,6 @@ function abrirModalNovoPerfil() { nomeNovoPerfil = ''; descricaoNovoPerfil = ''; - setorNovoPerfil = ''; - nivelNovoPerfil = 3; roleParaDuplicar = ''; modalNovoPerfilAberto = true; } @@ -173,16 +242,12 @@ const nome = nomeNovoPerfil.trim(); const descricao = descricaoNovoPerfil.trim(); - const setor = setorNovoPerfil.trim(); - const nivel = Math.min(Math.max(Math.round(Number(nivelNovoPerfil)), 0), 10); criandoNovoPerfil = true; try { const resultado = await client.mutation(api.roles.criar, { nome, descricao, - nivel, - setor: setor.length > 0 ? setor : undefined, copiarDeRoleId: roleParaDuplicar || undefined }); @@ -211,7 +276,7 @@ } - + - {#if roleRow.nivel <= 1} + {#if roleRow.admin}
-
- - -
- -
- - - - Níveis menores representam maior privilégio (ex.: 0 = administrativo). - -
-