feat: enhance user management with matricula retrieval and validation
- Updated user-related queries and mutations to retrieve the matricula from associated funcionario records, improving data accuracy. - Refactored user creation and listing functionalities to ensure matricula is correctly handled and displayed. - Enhanced error handling and validation for user operations, ensuring a more robust user management experience. - Improved the overall structure of user-related code for better maintainability and clarity.
This commit is contained in:
@@ -4,6 +4,21 @@ import { hashPassword, generateToken } from "./auth/utils";
|
||||
import { registrarAtividade } from "./logsAtividades";
|
||||
import { Id, Doc } from "./_generated/dataModel";
|
||||
import { api } from "./_generated/api";
|
||||
import type { QueryCtx } from "./_generated/server";
|
||||
|
||||
/**
|
||||
* Helper para obter a matrícula do usuário (do funcionário se houver)
|
||||
*/
|
||||
async function obterMatriculaUsuario(
|
||||
ctx: QueryCtx,
|
||||
usuario: Doc<"usuarios">
|
||||
): Promise<string | undefined> {
|
||||
if (usuario.funcionarioId) {
|
||||
const funcionario = await ctx.db.get(usuario.funcionarioId);
|
||||
return funcionario?.matricula;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associar funcionário a um usuário
|
||||
@@ -30,8 +45,11 @@ export const associarFuncionario = mutation({
|
||||
.first();
|
||||
|
||||
if (usuarioExistente && usuarioExistente._id !== args.usuarioId) {
|
||||
const matricula = await obterMatriculaUsuario(ctx, usuarioExistente);
|
||||
throw new Error(
|
||||
`Este funcionário já está associado ao usuário: ${usuarioExistente.nome} (${usuarioExistente.matricula})`
|
||||
`Este funcionário já está associado ao usuário: ${
|
||||
usuarioExistente.nome
|
||||
}${matricula ? ` (${matricula})` : ""}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,7 +84,6 @@ export const desassociarFuncionario = mutation({
|
||||
*/
|
||||
export const criar = mutation({
|
||||
args: {
|
||||
matricula: v.string(),
|
||||
nome: v.string(),
|
||||
email: v.string(),
|
||||
roleId: v.id("roles"),
|
||||
@@ -78,16 +95,6 @@ export const criar = mutation({
|
||||
v.object({ sucesso: v.literal(false), erro: v.string() })
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
// Verificar se matrícula já existe
|
||||
const existente = await ctx.db
|
||||
.query("usuarios")
|
||||
.withIndex("by_matricula", (q) => q.eq("matricula", args.matricula))
|
||||
.first();
|
||||
|
||||
if (existente) {
|
||||
return { sucesso: false as const, erro: "Matrícula já cadastrada" };
|
||||
}
|
||||
|
||||
// Verificar se email já existe
|
||||
const emailExistente = await ctx.db
|
||||
.query("usuarios")
|
||||
@@ -103,7 +110,6 @@ export const criar = mutation({
|
||||
|
||||
// Criar usuário
|
||||
const usuarioId = await ctx.db.insert("usuarios", {
|
||||
matricula: args.matricula,
|
||||
senhaHash,
|
||||
nome: args.nome,
|
||||
email: args.email,
|
||||
@@ -194,9 +200,17 @@ export const listar = query({
|
||||
handler: async (ctx, args) => {
|
||||
let usuarios = await ctx.db.query("usuarios").collect();
|
||||
|
||||
// Filtrar por matrícula
|
||||
// Filtrar por matrícula (buscar no funcionário)
|
||||
if (args.matricula) {
|
||||
usuarios = usuarios.filter((u) => u.matricula.includes(args.matricula!));
|
||||
const usuariosComMatricula = await Promise.all(
|
||||
usuarios.map(async (u) => {
|
||||
const matricula = await obterMatriculaUsuario(ctx, u);
|
||||
return { usuario: u, matricula };
|
||||
})
|
||||
);
|
||||
usuarios = usuariosComMatricula
|
||||
.filter(({ matricula }) => matricula?.includes(args.matricula!))
|
||||
.map(({ usuario }) => usuario);
|
||||
}
|
||||
|
||||
// Filtrar por ativo
|
||||
@@ -206,20 +220,25 @@ export const listar = query({
|
||||
|
||||
// Buscar roles e funcionários
|
||||
const resultado = [];
|
||||
const usuariosSemRole: Array<{ nome: string; matricula: string; roleId: Id<"roles"> }> = [];
|
||||
const usuariosSemRole: Array<{
|
||||
nome: string;
|
||||
matricula: string;
|
||||
roleId: Id<"roles">;
|
||||
}> = [];
|
||||
|
||||
for (const usuario of usuarios) {
|
||||
try {
|
||||
const role = await ctx.db.get(usuario.roleId);
|
||||
|
||||
|
||||
// Se a role não existe, criar uma role de erro mas ainda incluir o usuário
|
||||
if (!role) {
|
||||
const matricula = await obterMatriculaUsuario(ctx, usuario);
|
||||
usuariosSemRole.push({
|
||||
nome: usuario.nome,
|
||||
matricula: usuario.matricula,
|
||||
matricula: matricula || "N/A",
|
||||
roleId: usuario.roleId,
|
||||
});
|
||||
|
||||
|
||||
// Filtrar por setor - se filtro está ativo e role não existe, pular
|
||||
if (args.setor) {
|
||||
continue;
|
||||
@@ -240,14 +259,19 @@ export const listar = query({
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Erro ao buscar funcionário ${usuario.funcionarioId} para usuário ${usuario._id}:`, error);
|
||||
console.error(
|
||||
`Erro ao buscar funcionário ${usuario.funcionarioId} para usuário ${usuario._id}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const matriculaUsuario = await obterMatriculaUsuario(ctx, usuario);
|
||||
|
||||
// Criar role de erro (sem _creationTime pois a role não existe)
|
||||
resultado.push({
|
||||
_id: usuario._id,
|
||||
matricula: usuario.matricula,
|
||||
matricula: matriculaUsuario,
|
||||
nome: usuario.nome,
|
||||
email: usuario.email,
|
||||
ativo: usuario.ativo,
|
||||
@@ -294,7 +318,10 @@ export const listar = query({
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Erro ao buscar funcionário ${usuario.funcionarioId} para usuário ${usuario._id}:`, error);
|
||||
console.error(
|
||||
`Erro ao buscar funcionário ${usuario.funcionarioId} para usuário ${usuario._id}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,14 +332,18 @@ export const listar = query({
|
||||
nome: role.nome,
|
||||
nivel: role.nivel,
|
||||
...(role.criadoPor !== undefined && { criadoPor: role.criadoPor }),
|
||||
...(role.customizado !== undefined && { customizado: role.customizado }),
|
||||
...(role.customizado !== undefined && {
|
||||
customizado: role.customizado,
|
||||
}),
|
||||
...(role.editavel !== undefined && { editavel: role.editavel }),
|
||||
...(role.setor !== undefined && { setor: role.setor }),
|
||||
};
|
||||
|
||||
const matriculaUsuario = await obterMatriculaUsuario(ctx, usuario);
|
||||
|
||||
resultado.push({
|
||||
_id: usuario._id,
|
||||
matricula: usuario.matricula,
|
||||
matricula: matriculaUsuario,
|
||||
nome: usuario.nome,
|
||||
email: usuario.email,
|
||||
ativo: usuario.ativo,
|
||||
@@ -334,7 +365,12 @@ export const listar = query({
|
||||
if (usuariosSemRole.length > 0) {
|
||||
console.warn(
|
||||
`⚠️ Encontrados ${usuariosSemRole.length} usuário(s) com perfil ausente:`,
|
||||
usuariosSemRole.map((u) => `${u.nome} (${u.matricula}) - RoleID: ${u.roleId}`)
|
||||
usuariosSemRole.map(
|
||||
(u) =>
|
||||
`${u.nome}${
|
||||
u.matricula !== "N/A" ? ` (${u.matricula})` : ""
|
||||
} - RoleID: ${u.roleId}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -559,7 +595,9 @@ export const atualizarPerfil = mutation({
|
||||
}
|
||||
|
||||
// Atualizar apenas os campos fornecidos
|
||||
const updates: Partial<Doc<"usuarios">> & { atualizadoEm: number } = { atualizadoEm: Date.now() };
|
||||
const updates: Partial<Doc<"usuarios">> & { atualizadoEm: number } = {
|
||||
atualizadoEm: Date.now(),
|
||||
};
|
||||
|
||||
if (args.avatar !== undefined) updates.avatar = args.avatar;
|
||||
if (args.fotoPerfil !== undefined) updates.fotoPerfil = args.fotoPerfil;
|
||||
@@ -591,7 +629,7 @@ export const obterPerfil = query({
|
||||
_id: v.id("usuarios"),
|
||||
nome: v.string(),
|
||||
email: v.string(),
|
||||
matricula: v.string(),
|
||||
matricula: v.optional(v.string()),
|
||||
funcionarioId: v.optional(v.id("funcionarios")),
|
||||
avatar: v.optional(v.string()),
|
||||
fotoPerfil: v.optional(v.id("_storage")),
|
||||
@@ -675,11 +713,13 @@ export const obterPerfil = query({
|
||||
fotoPerfilUrl = await ctx.storage.getUrl(usuarioAtual.fotoPerfil);
|
||||
}
|
||||
|
||||
const matricula = await obterMatriculaUsuario(ctx, usuarioAtual);
|
||||
|
||||
return {
|
||||
_id: usuarioAtual._id,
|
||||
nome: usuarioAtual.nome,
|
||||
email: usuarioAtual.email,
|
||||
matricula: usuarioAtual.matricula,
|
||||
matricula: matricula || undefined,
|
||||
funcionarioId: usuarioAtual.funcionarioId,
|
||||
avatar: usuarioAtual.avatar,
|
||||
fotoPerfil: usuarioAtual.fotoPerfil,
|
||||
@@ -735,11 +775,13 @@ export const listarParaChat = query({
|
||||
fotoPerfilUrl = await ctx.storage.getUrl(usuario.fotoPerfil);
|
||||
}
|
||||
|
||||
const matricula = await obterMatriculaUsuario(ctx, usuario);
|
||||
|
||||
return {
|
||||
_id: usuario._id,
|
||||
nome: usuario.nome,
|
||||
email: usuario.email,
|
||||
matricula: usuario.matricula || undefined,
|
||||
matricula: matricula || undefined,
|
||||
avatar: usuario.avatar,
|
||||
fotoPerfil: usuario.fotoPerfil,
|
||||
fotoPerfilUrl,
|
||||
@@ -1035,7 +1077,6 @@ export const editarUsuario = mutation({
|
||||
*/
|
||||
export const criarAdminMaster = mutation({
|
||||
args: {
|
||||
matricula: v.string(),
|
||||
nome: v.string(),
|
||||
email: v.string(),
|
||||
senha: v.optional(v.string()),
|
||||
@@ -1074,32 +1115,9 @@ export const criarAdminMaster = mutation({
|
||||
};
|
||||
}
|
||||
|
||||
// Se já existir usuário por matrícula, promove/atualiza
|
||||
const existentePorMatricula = await ctx.db
|
||||
.query("usuarios")
|
||||
.withIndex("by_matricula", (q) => q.eq("matricula", args.matricula))
|
||||
.first();
|
||||
|
||||
const senhaTemporaria = args.senha || gerarSenhaTemporaria();
|
||||
const senhaHash = await hashPassword(senhaTemporaria);
|
||||
|
||||
if (existentePorMatricula) {
|
||||
await ctx.db.patch(existentePorMatricula._id, {
|
||||
nome: args.nome,
|
||||
email: args.email,
|
||||
senhaHash,
|
||||
roleId: roleTIMaster._id,
|
||||
ativo: true,
|
||||
primeiroAcesso: true,
|
||||
atualizadoEm: Date.now(),
|
||||
});
|
||||
return {
|
||||
sucesso: true as const,
|
||||
usuarioId: existentePorMatricula._id,
|
||||
senhaTemporaria,
|
||||
};
|
||||
}
|
||||
|
||||
// Verificar se email já existe
|
||||
const existentePorEmail = await ctx.db
|
||||
.query("usuarios")
|
||||
@@ -1108,7 +1126,6 @@ export const criarAdminMaster = mutation({
|
||||
if (existentePorEmail) {
|
||||
// Promove usuário existente por email
|
||||
await ctx.db.patch(existentePorEmail._id, {
|
||||
matricula: args.matricula,
|
||||
nome: args.nome,
|
||||
senhaHash,
|
||||
roleId: roleTIMaster._id,
|
||||
@@ -1125,7 +1142,6 @@ export const criarAdminMaster = mutation({
|
||||
|
||||
// Criar novo usuário TI Master
|
||||
const usuarioId = await ctx.db.insert("usuarios", {
|
||||
matricula: args.matricula,
|
||||
senhaHash,
|
||||
nome: args.nome,
|
||||
email: args.email,
|
||||
@@ -1194,7 +1210,6 @@ export const excluirUsuarioLogico = mutation({
|
||||
*/
|
||||
export const criarUsuarioCompleto = mutation({
|
||||
args: {
|
||||
matricula: v.string(),
|
||||
nome: v.string(),
|
||||
email: v.string(),
|
||||
roleId: v.id("roles"),
|
||||
@@ -1212,16 +1227,6 @@ export const criarUsuarioCompleto = mutation({
|
||||
v.object({ sucesso: v.literal(false), erro: v.string() })
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
// Verificar se matrícula já existe
|
||||
const existente = await ctx.db
|
||||
.query("usuarios")
|
||||
.withIndex("by_matricula", (q) => q.eq("matricula", args.matricula))
|
||||
.first();
|
||||
|
||||
if (existente) {
|
||||
return { sucesso: false as const, erro: "Matrícula já cadastrada" };
|
||||
}
|
||||
|
||||
// Verificar se email já existe
|
||||
const emailExistente = await ctx.db
|
||||
.query("usuarios")
|
||||
@@ -1238,7 +1243,6 @@ export const criarUsuarioCompleto = mutation({
|
||||
|
||||
// Criar usuário
|
||||
const usuarioId = await ctx.db.insert("usuarios", {
|
||||
matricula: args.matricula,
|
||||
senhaHash,
|
||||
nome: args.nome,
|
||||
email: args.email,
|
||||
@@ -1256,7 +1260,7 @@ export const criarUsuarioCompleto = mutation({
|
||||
args.criadoPorId,
|
||||
"criar",
|
||||
"usuarios",
|
||||
JSON.stringify({ usuarioId, matricula: args.matricula, nome: args.nome }),
|
||||
JSON.stringify({ usuarioId, nome: args.nome }),
|
||||
usuarioId
|
||||
);
|
||||
|
||||
@@ -1272,7 +1276,6 @@ export const criarUsuarioCompleto = mutation({
|
||||
*/
|
||||
export const criarAdminPadrao = mutation({
|
||||
args: {
|
||||
matricula: v.optional(v.string()),
|
||||
nome: v.optional(v.string()),
|
||||
email: v.optional(v.string()),
|
||||
senha: v.optional(v.string()),
|
||||
@@ -1282,7 +1285,6 @@ export const criarAdminPadrao = mutation({
|
||||
usuarioId: v.optional(v.id("usuarios")),
|
||||
}),
|
||||
handler: async (ctx, args) => {
|
||||
const matricula = args.matricula ?? "0000";
|
||||
const nome = args.nome ?? "Administrador Geral";
|
||||
const email = args.email ?? "admin@sgse.pe.gov.br";
|
||||
const senha = args.senha ?? "Admin@123";
|
||||
@@ -1306,12 +1308,7 @@ export const criarAdminPadrao = mutation({
|
||||
|
||||
if (!roleAdmin) return { sucesso: false };
|
||||
|
||||
// Verificar se já existe por matrícula ou email
|
||||
const existentePorMatricula = await ctx.db
|
||||
.query("usuarios")
|
||||
.withIndex("by_matricula", (q) => q.eq("matricula", matricula))
|
||||
.first();
|
||||
|
||||
// Verificar se já existe por email
|
||||
const existentePorEmail = await ctx.db
|
||||
.query("usuarios")
|
||||
.withIndex("by_email", (q) => q.eq("email", email))
|
||||
@@ -1319,10 +1316,8 @@ export const criarAdminPadrao = mutation({
|
||||
|
||||
const senhaHash = await hashPassword(senha);
|
||||
|
||||
if (existentePorMatricula || existentePorEmail) {
|
||||
const alvo = existentePorMatricula ?? existentePorEmail!;
|
||||
await ctx.db.patch(alvo._id, {
|
||||
matricula,
|
||||
if (existentePorEmail) {
|
||||
await ctx.db.patch(existentePorEmail._id, {
|
||||
nome,
|
||||
email,
|
||||
senhaHash,
|
||||
@@ -1331,11 +1326,10 @@ export const criarAdminPadrao = mutation({
|
||||
primeiroAcesso: false,
|
||||
atualizadoEm: Date.now(),
|
||||
});
|
||||
return { sucesso: true, usuarioId: alvo._id };
|
||||
return { sucesso: true, usuarioId: existentePorEmail._id };
|
||||
}
|
||||
|
||||
const usuarioId = await ctx.db.insert("usuarios", {
|
||||
matricula,
|
||||
senhaHash,
|
||||
nome,
|
||||
email,
|
||||
|
||||
Reference in New Issue
Block a user