import { v } from "convex/values"; import { mutation, query } from "./_generated/server"; import type { Id } from "./_generated/dataModel"; /** * Lista de menus do sistema */ export const MENUS_SISTEMA = [ { path: "/recursos-humanos", nome: "Recursos Humanos", descricao: "Gestão de funcionários e símbolos" }, { path: "/recursos-humanos/funcionarios", nome: "Funcionários", descricao: "Cadastro e gestão de funcionários" }, { path: "/recursos-humanos/simbolos", nome: "Símbolos", descricao: "Cadastro e gestão de símbolos" }, { path: "/financeiro", nome: "Financeiro", descricao: "Gestão financeira" }, { path: "/controladoria", nome: "Controladoria", descricao: "Controle e auditoria" }, { path: "/licitacoes", nome: "Licitações", descricao: "Gestão de licitações" }, { path: "/compras", nome: "Compras", descricao: "Gestão de compras" }, { path: "/juridico", nome: "Jurídico", descricao: "Departamento jurídico" }, { path: "/comunicacao", nome: "Comunicação", descricao: "Gestão de comunicação" }, { path: "/programas-esportivos", nome: "Programas Esportivos", descricao: "Gestão de programas esportivos" }, { path: "/secretaria-executiva", nome: "Secretaria Executiva", descricao: "Secretaria executiva" }, { path: "/gestao-pessoas", nome: "Gestão de Pessoas", descricao: "Gestão de recursos humanos" }, { path: "/ti", nome: "Tecnologia da Informação", descricao: "TI e suporte técnico" }, { path: "/ti/painel-administrativo", nome: "Painel Administrativo TI", descricao: "Painel de administração do sistema" }, ] as const; /** * Listar todas as permissões de menu para uma role */ export const listarPorRole = query({ args: { roleId: v.id("roles") }, returns: v.array( v.object({ _id: v.id("menuPermissoes"), roleId: v.id("roles"), menuPath: v.string(), podeAcessar: v.boolean(), podeConsultar: v.boolean(), podeGravar: v.boolean(), }) ), handler: async (ctx, args) => { return await ctx.db .query("menuPermissoes") .withIndex("by_role", (q) => q.eq("roleId", args.roleId)) .collect(); }, }); /** * Verificar se um usuário tem permissão para acessar um menu * Prioridade: Permissão personalizada > Permissão da role */ export const verificarAcesso = query({ args: { usuarioId: v.id("usuarios"), menuPath: v.string(), }, returns: v.object({ podeAcessar: v.boolean(), podeConsultar: v.boolean(), podeGravar: v.boolean(), motivo: v.optional(v.string()), }), handler: async (ctx, args) => { // Buscar o usuário const usuario = await ctx.db.get(args.usuarioId); if (!usuario) { return { podeAcessar: false, podeConsultar: false, podeGravar: false, motivo: "Usuário não encontrado", }; } // Verificar se o usuário está ativo if (!usuario.ativo) { return { podeAcessar: false, podeConsultar: false, podeGravar: false, motivo: "Usuário inativo", }; } // Buscar a role do usuário const role = await ctx.db.get(usuario.roleId); if (!role) { return { podeAcessar: false, podeConsultar: false, podeGravar: false, motivo: "Role não encontrada", }; } // Apenas TI_MASTER (nível 0) tem acesso total irrestrito // Admin, TI_USUARIO e outros (nível >= 1) têm permissões configuráveis if (role.nivel === 0) { return { podeAcessar: true, podeConsultar: true, podeGravar: true, }; } // Dashboard e Solicitar Acesso são públicos if (args.menuPath === "/" || args.menuPath === "/solicitar-acesso") { return { podeAcessar: true, podeConsultar: true, podeGravar: false, }; } // 1. Verificar se existe permissão personalizada para este usuário const permissaoPersonalizada = await ctx.db .query("menuPermissoesPersonalizadas") .withIndex("by_usuario_and_menu", (q) => q.eq("usuarioId", args.usuarioId).eq("menuPath", args.menuPath) ) .first(); if (permissaoPersonalizada) { return { podeAcessar: permissaoPersonalizada.podeAcessar, podeConsultar: permissaoPersonalizada.podeConsultar, podeGravar: permissaoPersonalizada.podeGravar, }; } // 2. Se não houver permissão personalizada, verificar permissão da role const permissaoRole = await ctx.db .query("menuPermissoes") .withIndex("by_role_and_menu", (q) => q.eq("roleId", usuario.roleId).eq("menuPath", args.menuPath) ) .first(); if (!permissaoRole) { return { podeAcessar: false, podeConsultar: false, podeGravar: false, motivo: "Sem permissão configurada para este menu", }; } return { podeAcessar: permissaoRole.podeAcessar, podeConsultar: permissaoRole.podeConsultar, podeGravar: permissaoRole.podeGravar, }; }, }); /** * Atualizar ou criar permissão de menu para uma role */ export const atualizarPermissao = mutation({ args: { roleId: v.id("roles"), menuPath: v.string(), podeAcessar: v.boolean(), podeConsultar: v.boolean(), podeGravar: v.boolean(), }, returns: v.id("menuPermissoes"), handler: async (ctx, args) => { // Verificar se já existe uma permissão const existente = await ctx.db .query("menuPermissoes") .withIndex("by_role_and_menu", (q) => q.eq("roleId", args.roleId).eq("menuPath", args.menuPath) ) .first(); if (existente) { // Atualizar permissão existente await ctx.db.patch(existente._id, { podeAcessar: args.podeAcessar, podeConsultar: args.podeConsultar, podeGravar: args.podeGravar, }); return existente._id; } else { // Criar nova permissão return await ctx.db.insert("menuPermissoes", { roleId: args.roleId, menuPath: args.menuPath, podeAcessar: args.podeAcessar, podeConsultar: args.podeConsultar, podeGravar: args.podeGravar, }); } }, }); /** * Remover permissão de menu */ export const removerPermissao = mutation({ args: { permissaoId: v.id("menuPermissoes"), }, returns: v.null(), handler: async (ctx, args) => { await ctx.db.delete(args.permissaoId); return null; }, }); /** * Inicializar permissões padrão para uma role */ export const inicializarPermissoesRole = mutation({ args: { roleId: v.id("roles"), }, returns: v.null(), handler: async (ctx, args) => { // Buscar a role const role = await ctx.db.get(args.roleId); if (!role) { throw new Error("Role não encontrada"); } // Admin e TI não precisam de permissões específicas (acesso total) if (role.nivel <= 1) { return null; } // Para outras roles, criar permissões básicas (apenas consulta) for (const menu of MENUS_SISTEMA) { // Verificar se já existe permissão const existente = await ctx.db .query("menuPermissoes") .withIndex("by_role_and_menu", (q) => q.eq("roleId", args.roleId).eq("menuPath", menu.path) ) .first(); if (!existente) { // Criar permissão padrão (sem acesso) await ctx.db.insert("menuPermissoes", { roleId: args.roleId, menuPath: menu.path, podeAcessar: false, podeConsultar: false, podeGravar: false, }); } } return null; }, }); /** * Listar todos os menus do sistema */ export const listarMenus = query({ args: {}, returns: v.array( v.object({ path: v.string(), nome: v.string(), descricao: v.string(), }) ), handler: async (ctx) => { return MENUS_SISTEMA.map((menu) => ({ path: menu.path, nome: menu.nome, descricao: menu.descricao, })); }, }); /** * Obter matriz de permissões (role x menu) para o painel de controle */ export const obterMatrizPermissoes = query({ args: {}, returns: v.array( v.object({ role: v.object({ _id: v.id("roles"), nome: v.string(), nivel: v.number(), descricao: v.string(), }), permissoes: v.array( v.object({ menuPath: v.string(), menuNome: v.string(), podeAcessar: v.boolean(), podeConsultar: v.boolean(), podeGravar: v.boolean(), permissaoId: v.optional(v.id("menuPermissoes")), }) ), }) ), handler: async (ctx) => { // Buscar todas as roles // TI_MASTER (nível 0) aparece mas não é editável // Admin, TI_USUARIO e outros (nível >= 1) são configuráveis const roles = await ctx.db.query("roles").collect(); const matriz = []; for (const role of roles) { const permissoes = []; for (const menu of MENUS_SISTEMA) { // Buscar permissão específica const permissao = await ctx.db .query("menuPermissoes") .withIndex("by_role_and_menu", (q) => q.eq("roleId", role._id).eq("menuPath", menu.path) ) .first(); // Admin e TI têm acesso total automático if (role.nivel <= 1) { permissoes.push({ menuPath: menu.path, menuNome: menu.nome, podeAcessar: true, podeConsultar: true, podeGravar: true, permissaoId: permissao?._id, }); } else { permissoes.push({ menuPath: menu.path, menuNome: menu.nome, podeAcessar: permissao?.podeAcessar ?? false, podeConsultar: permissao?.podeConsultar ?? false, podeGravar: permissao?.podeGravar ?? false, permissaoId: permissao?._id, }); } } matriz.push({ role: { _id: role._id, nome: role.nome, nivel: role.nivel, descricao: role.descricao, }, permissoes, }); } return matriz; }, }); /** * Criar ou atualizar permissão personalizada por matrícula */ export const atualizarPermissaoPersonalizada = mutation({ args: { matricula: v.string(), menuPath: v.string(), podeAcessar: v.boolean(), podeConsultar: v.boolean(), podeGravar: v.boolean(), }, returns: v.union(v.id("menuPermissoesPersonalizadas"), v.null()), handler: async (ctx, args) => { // Buscar usuário pela matrícula const usuario = await ctx.db .query("usuarios") .withIndex("by_matricula", (q) => q.eq("matricula", args.matricula)) .first(); if (!usuario) { throw new Error("Usuário não encontrado com esta matrícula"); } // Verificar se já existe permissão personalizada const existente = await ctx.db .query("menuPermissoesPersonalizadas") .withIndex("by_usuario_and_menu", (q) => q.eq("usuarioId", usuario._id).eq("menuPath", args.menuPath) ) .first(); if (existente) { // Atualizar permissão existente await ctx.db.patch(existente._id, { podeAcessar: args.podeAcessar, podeConsultar: args.podeConsultar, podeGravar: args.podeGravar, }); return existente._id; } else { // Criar nova permissão return await ctx.db.insert("menuPermissoesPersonalizadas", { usuarioId: usuario._id, matricula: args.matricula, menuPath: args.menuPath, podeAcessar: args.podeAcessar, podeConsultar: args.podeConsultar, podeGravar: args.podeGravar, }); } }, }); /** * Remover permissão personalizada */ export const removerPermissaoPersonalizada = mutation({ args: { permissaoId: v.id("menuPermissoesPersonalizadas"), }, returns: v.null(), handler: async (ctx, args) => { await ctx.db.delete(args.permissaoId); return null; }, }); /** * Listar permissões personalizadas de um usuário por matrícula */ export const listarPermissoesPersonalizadas = query({ args: { matricula: v.string(), }, returns: v.array( v.object({ _id: v.id("menuPermissoesPersonalizadas"), menuPath: v.string(), menuNome: v.string(), podeAcessar: v.boolean(), podeConsultar: v.boolean(), podeGravar: v.boolean(), }) ), handler: async (ctx, args) => { // Buscar usuário const usuario = await ctx.db .query("usuarios") .withIndex("by_matricula", (q) => q.eq("matricula", args.matricula)) .first(); if (!usuario) { return []; } // Buscar permissões personalizadas const permissoes = await ctx.db .query("menuPermissoesPersonalizadas") .withIndex("by_usuario", (q) => q.eq("usuarioId", usuario._id)) .collect(); // Mapear com nomes dos menus return permissoes.map((p) => { const menu = MENUS_SISTEMA.find((m) => m.path === p.menuPath); return { _id: p._id, menuPath: p.menuPath, menuNome: menu?.nome || p.menuPath, podeAcessar: p.podeAcessar, podeConsultar: p.podeConsultar, podeGravar: p.podeGravar, }; }); }, }); /** * Buscar usuário por matrícula para o painel de personalização */ export const buscarUsuarioPorMatricula = query({ args: { matricula: v.string(), }, returns: v.union( v.object({ _id: v.id("usuarios"), matricula: v.string(), nome: v.string(), email: v.string(), role: v.object({ nome: v.string(), nivel: v.number(), descricao: v.string(), }), ativo: v.boolean(), }), v.null() ), handler: async (ctx, args) => { const usuario = await ctx.db .query("usuarios") .withIndex("by_matricula", (q) => q.eq("matricula", args.matricula)) .first(); if (!usuario) { return null; } const role = await ctx.db.get(usuario.roleId); if (!role) { return null; } return { _id: usuario._id, matricula: usuario.matricula, nome: usuario.nome, email: usuario.email, role: { nome: role.nome, nivel: role.nivel, descricao: role.descricao, }, ativo: usuario.ativo, }; }, });