Emp perfis #25
@@ -543,7 +543,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else if catalogoQuery.data}
|
{:else if catalogoQuery.data && catalogoQuery.data.length > 0}
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
{#each catalogoQuery.data as item (item.recurso)}
|
{#each catalogoQuery.data as item (item.recurso)}
|
||||||
{@const recursoExpandido = isRecursoExpandido(roleId, item.recurso)}
|
{@const recursoExpandido = isRecursoExpandido(roleId, item.recurso)}
|
||||||
@@ -576,8 +576,13 @@
|
|||||||
<!-- Lista de ações (visível quando expandido) -->
|
<!-- Lista de ações (visível quando expandido) -->
|
||||||
{#if recursoExpandido}
|
{#if recursoExpandido}
|
||||||
<div class="bg-base-100 border-base-300 border-t px-4 py-3">
|
<div class="bg-base-100 border-base-300 border-t px-4 py-3">
|
||||||
|
{#if item.acoes.length === 0}
|
||||||
|
<p class="text-base-content/60 text-sm">
|
||||||
|
Nenhuma permissão cadastrada para este recurso.
|
||||||
|
</p>
|
||||||
|
{:else}
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
{#each ['ver', 'listar', 'criar', 'editar', 'excluir'] as acao (acao)}
|
{#each item.acoes as acao (acao)}
|
||||||
<label
|
<label
|
||||||
class="hover:bg-base-200 flex cursor-pointer items-center gap-3 rounded p-2 transition-colors"
|
class="hover:bg-base-200 flex cursor-pointer items-center gap-3 rounded p-2 transition-colors"
|
||||||
>
|
>
|
||||||
@@ -589,15 +594,22 @@
|
|||||||
onchange={(e) =>
|
onchange={(e) =>
|
||||||
toggleAcao(roleId, item.recurso, acao, e.currentTarget.checked)}
|
toggleAcao(roleId, item.recurso, acao, e.currentTarget.checked)}
|
||||||
/>
|
/>
|
||||||
<span class="flex-1 font-medium capitalize">{acao}</span>
|
<span class="flex-1 font-medium">{acao}</span>
|
||||||
</label>
|
</label>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="alert alert-info mt-4">
|
||||||
|
<span class="font-semibold">
|
||||||
|
Nenhuma permissão cadastrada ainda. Use o botão “Criar permissão” para começar.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -41,19 +41,15 @@
|
|||||||
const stats = $derived.by(() => {
|
const stats = $derived.by(() => {
|
||||||
if (carregando) return null;
|
if (carregando) return null;
|
||||||
|
|
||||||
const porNivel = {
|
const nivelMaximo = roles.filter((r) => r.nivel === 0).length;
|
||||||
0: roles.filter((r) => r.nivel === 0).length,
|
const nivelAdministrativo = roles.filter((r) => r.nivel === 1).length;
|
||||||
1: roles.filter((r) => r.nivel === 1).length,
|
const niveisLegado = roles.filter((r) => r.nivel > 1).length;
|
||||||
2: roles.filter((r) => r.nivel === 2).length,
|
|
||||||
3: roles.filter((r) => r.nivel >= 3).length
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
total: roles.length,
|
total: roles.length,
|
||||||
nivelMaximo: porNivel[0],
|
nivelMaximo,
|
||||||
nivelAlto: porNivel[1],
|
nivelAdministrativo,
|
||||||
nivelMedio: porNivel[2],
|
niveisLegado,
|
||||||
nivelBaixo: porNivel[3],
|
|
||||||
comSetor: roles.filter((r) => r.setor).length
|
comSetor: roles.filter((r) => r.setor).length
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -78,10 +74,11 @@
|
|||||||
|
|
||||||
// Filtro por nível
|
// Filtro por nível
|
||||||
if (filtroNivel !== '') {
|
if (filtroNivel !== '') {
|
||||||
if (filtroNivel === 3) {
|
if (filtroNivel === 0 || filtroNivel === 1) {
|
||||||
resultado = resultado.filter((r) => r.nivel >= 3);
|
|
||||||
} else {
|
|
||||||
resultado = resultado.filter((r) => r.nivel === filtroNivel);
|
resultado = resultado.filter((r) => r.nivel === filtroNivel);
|
||||||
|
} else {
|
||||||
|
// Qualquer outro valor é considerado legado
|
||||||
|
resultado = resultado.filter((r) => r.nivel > 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,22 +93,19 @@
|
|||||||
function obterCorNivel(nivel: number): string {
|
function obterCorNivel(nivel: number): string {
|
||||||
if (nivel === 0) return 'badge-error';
|
if (nivel === 0) return 'badge-error';
|
||||||
if (nivel === 1) return 'badge-warning';
|
if (nivel === 1) return 'badge-warning';
|
||||||
if (nivel === 2) return 'badge-info';
|
// Níveis > 1 são considerados legado
|
||||||
return 'badge-ghost';
|
return 'badge-ghost';
|
||||||
}
|
}
|
||||||
|
|
||||||
function obterTextoNivel(nivel: number): string {
|
function obterTextoNivel(nivel: number): string {
|
||||||
if (nivel === 0) return 'Máximo';
|
if (nivel === 0) return 'Máximo';
|
||||||
if (nivel === 1) return 'Alto';
|
if (nivel === 1) return 'Administrativo';
|
||||||
if (nivel === 2) return 'Médio';
|
return `Legado (${nivel})`;
|
||||||
if (nivel === 3) return 'Baixo';
|
|
||||||
return `Nível ${nivel}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function obterCorCardNivel(nivel: number): string {
|
function obterCorCardNivel(nivel: number): string {
|
||||||
if (nivel === 0) return 'border-l-4 border-error';
|
if (nivel === 0) return 'border-l-4 border-error';
|
||||||
if (nivel === 1) return 'border-l-4 border-warning';
|
if (nivel === 1) return 'border-l-4 border-warning';
|
||||||
if (nivel === 2) return 'border-l-4 border-info';
|
|
||||||
return 'border-l-4 border-base-300';
|
return 'border-l-4 border-base-300';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,31 +170,24 @@
|
|||||||
|
|
||||||
<!-- Estatísticas -->
|
<!-- Estatísticas -->
|
||||||
{#if stats}
|
{#if stats}
|
||||||
<div class="mb-8 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-5">
|
<div class="mb-8 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||||
<StatsCard title="Total de Perfis" value={stats.total} Icon={Users} color="primary" />
|
<StatsCard title="Total de Perfis" value={stats.total} Icon={Users} color="primary" />
|
||||||
<StatsCard
|
<StatsCard
|
||||||
title="Nível Máximo"
|
title="Nível Máximo (0)"
|
||||||
value={stats.nivelMaximo}
|
value={stats.nivelMaximo}
|
||||||
description="Acesso total"
|
description="Acesso total ao sistema"
|
||||||
Icon={Shield}
|
Icon={Shield}
|
||||||
color="error"
|
color="error"
|
||||||
/>
|
/>
|
||||||
<StatsCard
|
<StatsCard
|
||||||
title="Nível Alto"
|
title="Nível Administrativo (1)"
|
||||||
value={stats.nivelAlto}
|
value={stats.nivelAdministrativo}
|
||||||
description="Acesso elevado"
|
description="Perfis administrativos com acesso total"
|
||||||
Icon={AlertTriangle}
|
Icon={AlertTriangle}
|
||||||
color="warning"
|
color="warning"
|
||||||
/>
|
/>
|
||||||
<StatsCard
|
<StatsCard
|
||||||
title="Nível Médio"
|
title="Perfis com Setor"
|
||||||
value={stats.nivelMedio}
|
|
||||||
description="Acesso padrão"
|
|
||||||
Icon={Info}
|
|
||||||
color="info"
|
|
||||||
/>
|
|
||||||
<StatsCard
|
|
||||||
title="Com Setor"
|
|
||||||
value={stats.comSetor}
|
value={stats.comSetor}
|
||||||
description={stats.total > 0
|
description={stats.total > 0
|
||||||
? ((stats.comSetor / stats.total) * 100).toFixed(0) + '% do total'
|
? ((stats.comSetor / stats.total) * 100).toFixed(0) + '% do total'
|
||||||
@@ -209,6 +196,18 @@
|
|||||||
color="secondary"
|
color="secondary"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{#if stats.niveisLegado > 0}
|
||||||
|
<div class="alert alert-warning mb-6">
|
||||||
|
<Info class="h-5 w-5" />
|
||||||
|
<div>
|
||||||
|
<h3 class="font-bold">Perfis com níveis legados</h3>
|
||||||
|
<p class="text-sm">
|
||||||
|
Existem {stats.niveisLegado} perfis com nível acima de 1. Esses perfis continuarão
|
||||||
|
sendo tratados como nível 1 (administrativo) após a migração.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Filtros -->
|
<!-- Filtros -->
|
||||||
|
|||||||
@@ -3,27 +3,271 @@ import { v } from 'convex/values';
|
|||||||
import type { Doc } from './_generated/dataModel';
|
import type { Doc } from './_generated/dataModel';
|
||||||
import { getCurrentUserFunction } from './auth';
|
import { getCurrentUserFunction } from './auth';
|
||||||
|
|
||||||
// Catálogo base de recursos e ações
|
// Catálogo de permissões base para seed controlado via mutation
|
||||||
// Ajuste/expanda conforme os módulos disponíveis no sistema
|
const PERMISSOES_BASE = {
|
||||||
export const CATALOGO_RECURSOS = [
|
permissoes: [
|
||||||
|
// Funcionários
|
||||||
{
|
{
|
||||||
|
nome: 'funcionarios.dashboard',
|
||||||
recurso: 'funcionarios',
|
recurso: 'funcionarios',
|
||||||
acoes: [
|
acao: 'dashboard',
|
||||||
'dashboard',
|
descricao: 'Acessar o painel de funcionários'
|
||||||
'ver',
|
|
||||||
'listar',
|
|
||||||
'criar',
|
|
||||||
'editar',
|
|
||||||
'excluir',
|
|
||||||
'aprovar_ausencias',
|
|
||||||
'aprovar_ferias'
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
nome: 'funcionarios.ver',
|
||||||
|
recurso: 'funcionarios',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Visualizar detalhes de funcionários'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'funcionarios.listar',
|
||||||
|
recurso: 'funcionarios',
|
||||||
|
acao: 'listar',
|
||||||
|
descricao: 'Listar funcionários'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'funcionarios.criar',
|
||||||
|
recurso: 'funcionarios',
|
||||||
|
acao: 'criar',
|
||||||
|
descricao: 'Criar novos funcionários'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'funcionarios.editar',
|
||||||
|
recurso: 'funcionarios',
|
||||||
|
acao: 'editar',
|
||||||
|
descricao: 'Editar dados de funcionários'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'funcionarios.excluir',
|
||||||
|
recurso: 'funcionarios',
|
||||||
|
acao: 'excluir',
|
||||||
|
descricao: 'Excluir funcionários'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'funcionarios.aprovar_ausencias',
|
||||||
|
recurso: 'funcionarios',
|
||||||
|
acao: 'aprovar_ausencias',
|
||||||
|
descricao: 'Aprovar ausências de funcionários'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'funcionarios.aprovar_ferias',
|
||||||
|
recurso: 'funcionarios',
|
||||||
|
acao: 'aprovar_ferias',
|
||||||
|
descricao: 'Aprovar férias de funcionários'
|
||||||
|
},
|
||||||
|
// Símbolos
|
||||||
|
{
|
||||||
|
nome: 'simbolos.dashboard',
|
||||||
recurso: 'simbolos',
|
recurso: 'simbolos',
|
||||||
acoes: ['dashboard', 'ver', 'listar', 'criar', 'editar', 'excluir']
|
acao: 'dashboard',
|
||||||
|
descricao: 'Acessar o painel de símbolos'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'simbolos.ver',
|
||||||
|
recurso: 'simbolos',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Visualizar detalhes de símbolos'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'simbolos.listar',
|
||||||
|
recurso: 'simbolos',
|
||||||
|
acao: 'listar',
|
||||||
|
descricao: 'Listar símbolos'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'simbolos.criar',
|
||||||
|
recurso: 'simbolos',
|
||||||
|
acao: 'criar',
|
||||||
|
descricao: 'Criar novos símbolos'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'simbolos.editar',
|
||||||
|
recurso: 'simbolos',
|
||||||
|
acao: 'editar',
|
||||||
|
descricao: 'Editar símbolos'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'simbolos.excluir',
|
||||||
|
recurso: 'simbolos',
|
||||||
|
acao: 'excluir',
|
||||||
|
descricao: 'Excluir símbolos'
|
||||||
|
},
|
||||||
|
// TI - Usuários
|
||||||
|
{
|
||||||
|
nome: 'ti_usuarios.listar',
|
||||||
|
recurso: 'ti_usuarios',
|
||||||
|
acao: 'listar',
|
||||||
|
descricao: 'Listar usuários do sistema'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'ti_usuarios.criar',
|
||||||
|
recurso: 'ti_usuarios',
|
||||||
|
acao: 'criar',
|
||||||
|
descricao: 'Criar novos usuários de acesso'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'ti_usuarios.editar',
|
||||||
|
recurso: 'ti_usuarios',
|
||||||
|
acao: 'editar',
|
||||||
|
descricao: 'Editar usuários de acesso'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'ti_usuarios.bloquear',
|
||||||
|
recurso: 'ti_usuarios',
|
||||||
|
acao: 'bloquear',
|
||||||
|
descricao: 'Bloquear ou desbloquear usuários'
|
||||||
|
},
|
||||||
|
// TI - Perfis
|
||||||
|
{
|
||||||
|
nome: 'ti_perfis.listar',
|
||||||
|
recurso: 'ti_perfis',
|
||||||
|
acao: 'listar',
|
||||||
|
descricao: 'Listar perfis de acesso'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'ti_perfis.criar',
|
||||||
|
recurso: 'ti_perfis',
|
||||||
|
acao: 'criar',
|
||||||
|
descricao: 'Criar novos perfis de acesso'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'ti_perfis.editar',
|
||||||
|
recurso: 'ti_perfis',
|
||||||
|
acao: 'editar',
|
||||||
|
descricao: 'Editar perfis de acesso'
|
||||||
|
},
|
||||||
|
// TI - Painel de Permissões
|
||||||
|
{
|
||||||
|
nome: 'ti_painel_permissoes.gerenciar',
|
||||||
|
recurso: 'ti_painel_permissoes',
|
||||||
|
acao: 'gerenciar',
|
||||||
|
descricao: 'Gerenciar matriz de permissões por perfil'
|
||||||
|
},
|
||||||
|
// TI - Solicitações de Acesso
|
||||||
|
{
|
||||||
|
nome: 'ti_solicitacoes_acesso.ver',
|
||||||
|
recurso: 'ti_solicitacoes_acesso',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Visualizar solicitações de acesso'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'ti_solicitacoes_acesso.aprovar',
|
||||||
|
recurso: 'ti_solicitacoes_acesso',
|
||||||
|
acao: 'aprovar',
|
||||||
|
descricao: 'Aprovar solicitações de acesso'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'ti_solicitacoes_acesso.reprovar',
|
||||||
|
recurso: 'ti_solicitacoes_acesso',
|
||||||
|
acao: 'reprovar',
|
||||||
|
descricao: 'Reprovar solicitações de acesso'
|
||||||
|
},
|
||||||
|
// TI - Configurações de E-mail
|
||||||
|
{
|
||||||
|
nome: 'ti_configuracoes_email.configurar',
|
||||||
|
recurso: 'ti_configuracoes_email',
|
||||||
|
acao: 'configurar',
|
||||||
|
descricao: 'Configurar parâmetros de envio de e-mail'
|
||||||
|
},
|
||||||
|
// TI - Monitoramento
|
||||||
|
{
|
||||||
|
nome: 'ti_monitoramento.ver',
|
||||||
|
recurso: 'ti_monitoramento',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar painel de monitoramento geral'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'ti_monitoramento_emails.ver',
|
||||||
|
recurso: 'ti_monitoramento_emails',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar monitoramento de envio de e-mails'
|
||||||
|
},
|
||||||
|
// TI - Notificações
|
||||||
|
{
|
||||||
|
nome: 'ti_notificacoes.configurar',
|
||||||
|
recurso: 'ti_notificacoes',
|
||||||
|
acao: 'configurar',
|
||||||
|
descricao: 'Configurar notificações do sistema'
|
||||||
|
},
|
||||||
|
// TI - Times
|
||||||
|
{
|
||||||
|
nome: 'ti_times.gerenciar',
|
||||||
|
recurso: 'ti_times',
|
||||||
|
acao: 'gerenciar',
|
||||||
|
descricao: 'Gerenciar times/equipes de TI'
|
||||||
|
},
|
||||||
|
// TI - Painel Administrativo
|
||||||
|
{
|
||||||
|
nome: 'ti_painel_administrativo.ver',
|
||||||
|
recurso: 'ti_painel_administrativo',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar painel administrativo de TI'
|
||||||
|
},
|
||||||
|
// Financeiro
|
||||||
|
{
|
||||||
|
nome: 'financeiro.ver',
|
||||||
|
recurso: 'financeiro',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar telas do módulo de financeiro'
|
||||||
|
},
|
||||||
|
// Controladoria
|
||||||
|
{
|
||||||
|
nome: 'controladoria.ver',
|
||||||
|
recurso: 'controladoria',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar telas do módulo de controladoria'
|
||||||
|
},
|
||||||
|
// Licitações
|
||||||
|
{
|
||||||
|
nome: 'licitacoes.ver',
|
||||||
|
recurso: 'licitacoes',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar telas do módulo de licitações'
|
||||||
|
},
|
||||||
|
// Compras
|
||||||
|
{
|
||||||
|
nome: 'compras.ver',
|
||||||
|
recurso: 'compras',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar telas do módulo de compras'
|
||||||
|
},
|
||||||
|
// Jurídico
|
||||||
|
{
|
||||||
|
nome: 'juridico.ver',
|
||||||
|
recurso: 'juridico',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar telas do módulo jurídico'
|
||||||
|
},
|
||||||
|
// Comunicação
|
||||||
|
{
|
||||||
|
nome: 'comunicacao.ver',
|
||||||
|
recurso: 'comunicacao',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar telas do módulo de comunicação'
|
||||||
|
},
|
||||||
|
// Programas Esportivos
|
||||||
|
{
|
||||||
|
nome: 'programas_esportivos.ver',
|
||||||
|
recurso: 'programas_esportivos',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar telas do módulo de programas esportivos'
|
||||||
|
},
|
||||||
|
// Secretaria Executiva
|
||||||
|
{
|
||||||
|
nome: 'secretaria_executiva.ver',
|
||||||
|
recurso: 'secretaria_executiva',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar telas do módulo de secretaria executiva'
|
||||||
|
},
|
||||||
|
// Gestão de Pessoas
|
||||||
|
{
|
||||||
|
nome: 'gestao_pessoas.ver',
|
||||||
|
recurso: 'gestao_pessoas',
|
||||||
|
acao: 'ver',
|
||||||
|
descricao: 'Acessar telas do módulo de gestão de pessoas'
|
||||||
}
|
}
|
||||||
] as const;
|
]
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const listarRecursosEAcoes = query({
|
export const listarRecursosEAcoes = query({
|
||||||
args: {},
|
args: {},
|
||||||
@@ -33,10 +277,18 @@ export const listarRecursosEAcoes = query({
|
|||||||
acoes: v.array(v.string())
|
acoes: v.array(v.string())
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
handler: async () => {
|
handler: async (ctx) => {
|
||||||
return CATALOGO_RECURSOS.map((r) => ({
|
const permissoes = await ctx.db.query('permissoes').collect();
|
||||||
recurso: r.recurso,
|
|
||||||
acoes: [...r.acoes]
|
const recursos: Record<string, Set<string>> = {};
|
||||||
|
for (const perm of permissoes) {
|
||||||
|
const set = (recursos[perm.recurso] ||= new Set<string>());
|
||||||
|
set.add(perm.acao);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.entries(recursos).map(([recurso, acoes]) => ({
|
||||||
|
recurso,
|
||||||
|
acoes: Array.from(acoes).sort()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -56,7 +308,7 @@ export const listarPermissoesAcoesPorRole = query({
|
|||||||
.withIndex('by_role', (q) => q.eq('roleId', args.roleId))
|
.withIndex('by_role', (q) => q.eq('roleId', args.roleId))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Carregar documentos de permissões
|
// Carregar documentos de permissões vinculadas a este role
|
||||||
const actionsByResource: Record<string, Set<string>> = {};
|
const actionsByResource: Record<string, Set<string>> = {};
|
||||||
for (const rp of rolePerms) {
|
for (const rp of rolePerms) {
|
||||||
const perm = await ctx.db.get(rp.permissaoId);
|
const perm = await ctx.db.get(rp.permissaoId);
|
||||||
@@ -65,13 +317,10 @@ export const listarPermissoesAcoesPorRole = query({
|
|||||||
set.add(perm.acao);
|
set.add(perm.acao);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalizar para todos os recursos do catálogo
|
return Object.entries(actionsByResource).map(([recurso, acoes]) => ({
|
||||||
const result: Array<{ recurso: string; acoes: Array<string> }> = [];
|
recurso,
|
||||||
for (const item of CATALOGO_RECURSOS) {
|
acoes: Array.from(acoes).sort()
|
||||||
const granted = Array.from(actionsByResource[item.recurso] ?? new Set<string>());
|
}));
|
||||||
result.push({ recurso: item.recurso, acoes: granted });
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -84,24 +333,12 @@ export const atualizarPermissaoAcao = mutation({
|
|||||||
},
|
},
|
||||||
returns: v.null(),
|
returns: v.null(),
|
||||||
handler: async (ctx, args) => {
|
handler: async (ctx, args) => {
|
||||||
// Garantir documento de permissão (recurso+acao)
|
// Buscar documento de permissão (recurso+acao)
|
||||||
let permissao = await ctx.db
|
const permissao = await ctx.db
|
||||||
.query('permissoes')
|
.query('permissoes')
|
||||||
.withIndex('by_recurso_e_acao', (q) => q.eq('recurso', args.recurso).eq('acao', args.acao))
|
.withIndex('by_recurso_e_acao', (q) => q.eq('recurso', args.recurso).eq('acao', args.acao))
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (!permissao) {
|
|
||||||
const nome = `${args.recurso}.${args.acao}`;
|
|
||||||
const descricao = `Permite ${args.acao} em ${args.recurso}`;
|
|
||||||
const id = await ctx.db.insert('permissoes', {
|
|
||||||
nome,
|
|
||||||
descricao,
|
|
||||||
recurso: args.recurso,
|
|
||||||
acao: args.acao
|
|
||||||
});
|
|
||||||
permissao = await ctx.db.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!permissao) return null;
|
if (!permissao) return null;
|
||||||
|
|
||||||
// Verificar vínculo atual
|
// Verificar vínculo atual
|
||||||
@@ -128,6 +365,36 @@ export const atualizarPermissaoAcao = mutation({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const seedPermissoesBase = mutation({
|
||||||
|
args: {},
|
||||||
|
returns: v.null(),
|
||||||
|
handler: async (ctx) => {
|
||||||
|
console.log('🔐 Seed de permissões base...');
|
||||||
|
|
||||||
|
for (const perm of PERMISSOES_BASE.permissoes) {
|
||||||
|
const existente = await ctx.db
|
||||||
|
.query('permissoes')
|
||||||
|
.withIndex('by_nome', (q) => q.eq('nome', perm.nome))
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (existente) {
|
||||||
|
console.log(` ℹ️ Permissão já existe: ${perm.nome}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.db.insert('permissoes', {
|
||||||
|
nome: perm.nome,
|
||||||
|
descricao: perm.descricao,
|
||||||
|
recurso: perm.recurso,
|
||||||
|
acao: perm.acao
|
||||||
|
});
|
||||||
|
console.log(` ✅ Permissão criada: ${perm.nome}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export const verificarAcao = query({
|
export const verificarAcao = query({
|
||||||
args: {
|
args: {
|
||||||
usuarioId: v.id('usuarios'),
|
usuarioId: v.id('usuarios'),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { v } from 'convex/values';
|
import { v } from 'convex/values';
|
||||||
import { query, mutation } from './_generated/server';
|
import { internalMutation, query, mutation } from './_generated/server';
|
||||||
import type { Id } from './_generated/dataModel';
|
import type { Id } from './_generated/dataModel';
|
||||||
import { getCurrentUserFunction } from './auth';
|
import { getCurrentUserFunction } from './auth';
|
||||||
|
|
||||||
@@ -98,13 +98,16 @@ export const criar = mutation({
|
|||||||
permissoesParaCopiar = permissoesOrigem.map((item) => item.permissaoId);
|
permissoesParaCopiar = permissoesOrigem.map((item) => item.permissaoId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nivelAjustado = Math.min(Math.max(Math.round(args.nivel), 0), 10);
|
// Agora só existem níveis 0 e 1.
|
||||||
|
// 0 = máximo (acesso total), 1 = administrativo (também com acesso total).
|
||||||
|
// Qualquer valor informado diferente de 0 é normalizado para 1.
|
||||||
|
const nivelNormalizado = Math.round(args.nivel) <= 0 ? 0 : 1;
|
||||||
const setor = args.setor?.trim();
|
const setor = args.setor?.trim();
|
||||||
|
|
||||||
const roleId = await ctx.db.insert('roles', {
|
const roleId = await ctx.db.insert('roles', {
|
||||||
nome: nomeNormalizado,
|
nome: nomeNormalizado,
|
||||||
descricao: args.descricao.trim() || args.nome.trim(),
|
descricao: args.descricao.trim() || args.nome.trim(),
|
||||||
nivel: nivelAjustado,
|
nivel: nivelNormalizado,
|
||||||
setor: setor && setor.length > 0 ? setor : undefined,
|
setor: setor && setor.length > 0 ? setor : undefined,
|
||||||
customizado: true,
|
customizado: true,
|
||||||
criadoPor: usuarioAtual._id,
|
criadoPor: usuarioAtual._id,
|
||||||
@@ -123,3 +126,26 @@ export const criar = mutation({
|
|||||||
return { sucesso: true as const, roleId };
|
return { sucesso: true as const, roleId };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migração de níveis de roles para o novo modelo (apenas 0 e 1).
|
||||||
|
* - Mantém níveis 0 e 1 como estão.
|
||||||
|
* - Converte qualquer nível > 1 para 1.
|
||||||
|
*/
|
||||||
|
export const migrarNiveisRoles = internalMutation({
|
||||||
|
args: {},
|
||||||
|
returns: v.null(),
|
||||||
|
handler: async (ctx) => {
|
||||||
|
const roles = await ctx.db.query('roles').collect();
|
||||||
|
|
||||||
|
for (const role of roles) {
|
||||||
|
if (role.nivel <= 1) continue;
|
||||||
|
|
||||||
|
await ctx.db.patch(role._id, {
|
||||||
|
nivel: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user