Fix usuarios page #6

Merged
killer-cf merged 28 commits from fix-usuarios-page into master 2025-11-04 17:42:21 +00:00
28 changed files with 1972 additions and 771 deletions
Showing only changes of commit 5cb63f9437 - Show all commits

View File

@@ -1,6 +1,7 @@
<script lang="ts">
import { useConvexClient } from "convex-svelte";
import { api } from "@sgse-app/backend/convex/_generated/api";
import type { Id, Doc } from "@sgse-app/backend/convex/_generated/dataModel";
interface Periodo {
dataInicio: string;
@@ -8,9 +9,14 @@
diasCorridos: number;
}
type SolicitacaoFerias = Doc<"solicitacoesFerias"> & {
funcionario?: Doc<"funcionarios"> | null;
gestor?: Doc<"usuarios"> | null;
};
interface Props {
solicitacao: any;
gestorId: string;
solicitacao: SolicitacaoFerias;
gestorId: Id<"usuarios">;
onSucesso?: () => void;
onCancelar?: () => void;
}
@@ -27,7 +33,7 @@
$effect(() => {
if (modoAjuste && periodos.length === 0) {
periodos = solicitacao.periodos.map((p: any) => ({...p}));
periodos = solicitacao.periodos.map((p) => ({...p}));
}
});
@@ -59,12 +65,12 @@
await client.mutation(api.ferias.aprovar, {
solicitacaoId: solicitacao._id,
gestorId: gestorId as any,
gestorId: gestorId,
});
if (onSucesso) onSucesso();
} catch (e: any) {
erro = e.message || "Erro ao aprovar solicitação";
} catch (e) {
erro = e instanceof Error ? e.message : String(e);
} finally {
processando = false;
}
@@ -82,13 +88,13 @@
await client.mutation(api.ferias.reprovar, {
solicitacaoId: solicitacao._id,
gestorId: gestorId as any,
gestorId: gestorId,
motivoReprovacao,
});
if (onSucesso) onSucesso();
} catch (e: any) {
erro = e.message || "Erro ao reprovar solicitação";
} catch (e) {
erro = e instanceof Error ? e.message : String(e);
} finally {
processando = false;
}
@@ -101,13 +107,13 @@
await client.mutation(api.ferias.ajustarEAprovar, {
solicitacaoId: solicitacao._id,
gestorId: gestorId as any,
gestorId: gestorId,
novosPeriodos: periodos,
});
if (onSucesso) onSucesso();
} catch (e: any) {
erro = e.message || "Erro ao ajustar e aprovar solicitação";
} catch (e) {
erro = e instanceof Error ? e.message : String(e);
} finally {
processando = false;
}

View File

@@ -110,10 +110,16 @@ class AuthStore {
{}
);
if (usuarioAtualizado && this.state.usuario) {
if (usuarioAtualizado) {
// Preservar role e primeiroAcesso do estado atual
this.state.usuario = {
...this.state.usuario,
...usuarioAtualizado,
role: this.state.usuario?.role || {
_id: "",
nome: "Usuário",
nivel: 999,
},
primeiroAcesso: this.state.usuario?.primeiroAcesso ?? false,
};
localStorage.setItem(

View File

@@ -39,7 +39,11 @@
}
});
$inspect(authStore.usuario?.funcionarioId);
// Debug: Verificar funcionarioId
$effect(() => {
console.log("🔍 [Perfil] funcionarioId:", authStore.usuario?.funcionarioId);
console.log("🔍 [Perfil] Usuário completo:", authStore.usuario);
});
// Queries
const funcionarioQuery = $derived(

View File

@@ -9,15 +9,16 @@ import {
} from "./auth/utils";
import { registrarLogin } from "./logsLogin";
import { Id } from "./_generated/dataModel";
import type { QueryCtx } from "./_generated/server";
/**
* Helper para verificar se usuário está bloqueado
*/
async function verificarBloqueioUsuario(ctx: any, usuarioId: Id<"usuarios">) {
async function verificarBloqueioUsuario(ctx: QueryCtx, usuarioId: Id<"usuarios">) {
const bloqueio = await ctx.db
.query("bloqueiosUsuarios")
.withIndex("by_usuario", (q: any) => q.eq("usuarioId", usuarioId))
.filter((q: any) => q.eq(q.field("ativo"), true))
.withIndex("by_usuario", (q) => q.eq("usuarioId", usuarioId))
.filter((q) => q.eq(q.field("ativo"), true))
.first();
return bloqueio !== null;
@@ -26,17 +27,17 @@ async function verificarBloqueioUsuario(ctx: any, usuarioId: Id<"usuarios">) {
/**
* Helper para verificar rate limiting por IP
*/
async function verificarRateLimitIP(ctx: any, ipAddress: string) {
async function verificarRateLimitIP(ctx: QueryCtx, ipAddress: string) {
// Últimas 15 minutos
const dataLimite = Date.now() - 15 * 60 * 1000;
const tentativas = await ctx.db
.query("logsLogin")
.withIndex("by_ip", (q: any) => q.eq("ipAddress", ipAddress))
.filter((q: any) => q.gte(q.field("timestamp"), dataLimite))
.withIndex("by_ip", (q) => q.eq("ipAddress", ipAddress))
.filter((q) => q.gte(q.field("timestamp"), dataLimite))
.collect();
const falhas = tentativas.filter((t: any) => !t.sucesso).length;
const falhas = tentativas.filter((t) => !t.sucesso).length;
// Bloquear se 5 ou mais tentativas falhas em 15 minutos
return falhas >= 5;
@@ -299,6 +300,7 @@ export const login = mutation({
matricula: usuario.matricula,
nome: usuario.nome,
email: usuario.email,
funcionarioId: usuario.funcionarioId,
role: {
_id: role._id,
nome: role.nome,

View File

@@ -20,11 +20,12 @@ async function getUsuarioAutenticado(ctx: QueryCtx | MutationCtx) {
.first();
}
// Se não encontrou via Better Auth, tentar via sessão
// Se não encontrou via Better Auth, tentar via sessão mais recente
if (!usuarioAtual) {
const sessaoAtiva = await ctx.db
.query("sessoes")
.filter((q) => q.eq(q.field("ativo"), true))
.order("desc")
.first();
if (sessaoAtiva) {
@@ -875,7 +876,7 @@ export const buscarMensagens = query({
c.participantes.includes(usuarioAtual._id)
);
let mensagens: any[] = [];
let mensagens: Doc<"mensagens">[] = [];
if (args.conversaId !== undefined) {
// Buscar em conversa específica

View File

@@ -106,7 +106,7 @@ export const salvarConfigEmail = mutation({
/**
* Testar conexão SMTP (action - precisa de Node.js)
*
*
* NOTA: Esta action será implementada quando instalarmos nodemailer.
* Por enquanto, retorna sucesso simulado para não bloquear o desenvolvimento.
*/
@@ -126,11 +126,14 @@ export const testarConexaoSMTP = action({
handler: async (ctx, args) => {
// TODO: Implementar teste real com nodemailer
// Por enquanto, simula sucesso
try {
// Validações básicas
if (!args.servidor || args.servidor.trim() === "") {
return { sucesso: false as const, erro: "Servidor SMTP não pode estar vazio" };
return {
sucesso: false as const,
erro: "Servidor SMTP não pode estar vazio",
};
}
if (args.porta < 1 || args.porta > 65535) {
@@ -141,10 +144,17 @@ export const testarConexaoSMTP = action({
await new Promise((resolve) => setTimeout(resolve, 1000));
// Retornar sucesso simulado
console.log("⚠️ AVISO: Teste de conexão SMTP simulado (nodemailer não instalado ainda)");
console.log(
"⚠️ AVISO: Teste de conexão SMTP simulado (nodemailer não instalado ainda)"
);
return { sucesso: true as const };
} catch (error: any) {
return { sucesso: false as const, erro: error.message || "Erro ao testar conexão" };
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
return {
sucesso: false as const,
erro: errorMessage || "Erro ao testar conexão",
};
}
},
});
@@ -162,5 +172,3 @@ export const marcarConfigTestada = mutation({
});
},
});

View File

@@ -27,7 +27,7 @@ export const updateDocumento = mutation({
// Atualizar o campo específico do documento
await ctx.db.patch(args.funcionarioId, {
[args.campo]: args.storageId,
} as any);
});
return null;
},
@@ -106,7 +106,7 @@ export const getDocumentosUrls = query({
];
for (const campo of campos) {
const storageId = (funcionario as any)[campo];
const storageId = funcionario[campo as keyof typeof funcionario] as Id<"_storage"> | undefined;
if (storageId) {
urls[campo] = await ctx.storage.getUrl(storageId);
} else {
@@ -114,7 +114,7 @@ export const getDocumentosUrls = query({
}
}
return urls as any;
return urls;
},
});

View File

@@ -8,7 +8,7 @@ import {
} from "./_generated/server";
import { Id } from "./_generated/dataModel";
import { renderizarTemplate } from "./templatesMensagens";
import { internal } from "./_generated/api";
import { internal, api } from "./_generated/api";
/**
* Enfileirar email para envio
@@ -58,7 +58,7 @@ export const enviarEmailComTemplate = mutation({
destinatario: v.string(),
destinatarioId: v.optional(v.id("usuarios")),
templateCodigo: v.string(),
variaveis: v.any(), // Record<string, string>
variaveis: v.record(v.string(), v.string()),
enviadoPorId: v.id("usuarios"),
},
returns: v.object({
@@ -113,7 +113,7 @@ export const listarFilaEmails = query({
),
limite: v.optional(v.number()),
},
returns: v.array(v.any()),
// Tipo inferido automaticamente pelo Convex
handler: async (ctx, args) => {
if (args.status) {
const emails = await ctx.db
@@ -166,7 +166,7 @@ export const reenviarEmail = mutation({
*/
export const getEmailById = internalQuery({
args: { emailId: v.id("notificacoesEmail") },
returns: v.union(v.any(), v.null()),
// Tipo inferido automaticamente pelo Convex
handler: async (ctx, args) => {
return await ctx.db.get(args.emailId);
},
@@ -174,7 +174,7 @@ export const getEmailById = internalQuery({
export const getActiveEmailConfig = internalQuery({
args: {},
returns: v.union(v.any(), v.null()),
// Tipo inferido automaticamente pelo Convex
handler: async (ctx) => {
return await ctx.db
.query("configuracaoEmail")
@@ -188,9 +188,10 @@ export const markEmailEnviando = internalMutation({
returns: v.null(),
handler: async (ctx, args) => {
const email = await ctx.db.get(args.emailId);
if (!email) return null;
await ctx.db.patch(args.emailId, {
status: "enviando",
tentativas: ((email as any)?.tentativas || 0) + 1,
tentativas: (email.tentativas || 0) + 1,
ultimaTentativa: Date.now(),
});
return null;
@@ -214,10 +215,11 @@ export const markEmailFalha = internalMutation({
returns: v.null(),
handler: async (ctx, args) => {
const email = await ctx.db.get(args.emailId);
if (!email) return null;
await ctx.db.patch(args.emailId, {
status: "falha",
erroDetalhes: args.erro,
tentativas: ((email as any)?.tentativas || 0) + 1,
tentativas: (email.tentativas || 0) + 1,
});
return null;
},
@@ -270,12 +272,12 @@ export const enviarEmailAction = action({
// Criar transporter do nodemailer
const transporter = nodemailer.createTransport({
host: (config as any).smtpHost,
port: (config as any).smtpPort,
secure: (config as any).smtpSecure,
host: config.servidor,
port: config.porta,
secure: config.usarSSL,
auth: {
user: (config as any).smtpUser,
pass: (config as any).smtpPassword,
user: config.usuario,
pass: config.senhaHash, // Note: em produção deve ser descriptografado
},
tls: {
rejectUnauthorized: false,
@@ -284,15 +286,15 @@ export const enviarEmailAction = action({
// Enviar email REAL
const info = await transporter.sendMail({
from: `"${(config as any).remetenteNome}" <${(config as any).remetenteEmail}>`,
to: (email as any).destinatario,
subject: (email as any).assunto,
html: (email as any).corpo,
from: `"${config.nomeRemetente}" <${config.emailRemetente}>`,
to: email.destinatario,
subject: email.assunto,
html: email.corpo,
});
console.log("✅ Email enviado com sucesso!");
console.log(" Para:", (email as any).destinatario);
console.log(" Assunto:", (email as any).assunto);
console.log(" Para:", email.destinatario);
console.log(" Assunto:", email.assunto);
console.log(" Message ID:", info.messageId);
// Marcar como enviado
@@ -301,16 +303,18 @@ export const enviarEmailAction = action({
});
return { sucesso: true };
} catch (error: any) {
console.error("❌ Erro ao enviar email:", error.message);
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
console.error("❌ Erro ao enviar email:", errorMessage);
// Marcar como falha
await ctx.runMutation(internal.email.markEmailFalha, {
emailId: args.emailId,
erro: error.message || "Erro ao enviar email",
erro: errorMessage,
});
return { sucesso: false, erro: error.message || "Erro ao enviar email" };
return { sucesso: false, erro: errorMessage };
}
},
});
@@ -342,7 +346,7 @@ export const processarFilaEmails = internalMutation({
// Agendar envio via action
// IMPORTANTE: Não podemos chamar action diretamente de mutation
// Por isso, usaremos o scheduler
// Por isso, usaremos o scheduler com string path
await ctx.scheduler.runAfter(0, "email:enviarEmailAction" as any, {
emailId: email._id,
});

View File

@@ -1,7 +1,7 @@
import { v } from "convex/values";
import { mutation, query, internalMutation } from "./_generated/server";
import { internal } from "./_generated/api";
import { Id } from "./_generated/dataModel";
import { Id, Doc } from "./_generated/dataModel";
// Validador para períodos
const periodoValidator = v.object({
@@ -11,9 +11,9 @@ const periodoValidator = v.object({
});
// Query: Listar TODAS as solicitações (para RH)
// Retorna tipo inferido automaticamente pelo Convex
export const listarTodas = query({
args: {},
returns: v.array(v.any()),
handler: async (ctx) => {
const solicitacoes = await ctx.db.query("solicitacoesFerias").collect();
@@ -52,7 +52,7 @@ export const listarTodas = query({
// Query: Listar solicitações do funcionário
export const listarMinhasSolicitacoes = query({
args: { funcionarioId: v.id("funcionarios") },
returns: v.array(v.any()),
// returns não especificado - TypeScript inferirá automaticamente o tipo correto
handler: async (ctx, args) => {
return await ctx.db
.query("solicitacoesFerias")
@@ -65,9 +65,9 @@ export const listarMinhasSolicitacoes = query({
});
// Query: Listar solicitações dos subordinados (para gestores)
// Retorna tipo inferido automaticamente pelo Convex
export const listarSolicitacoesSubordinados = query({
args: { gestorId: v.id("usuarios") },
returns: v.array(v.any()),
handler: async (ctx, args) => {
// Buscar times onde o usuário é gestor
const timesGestor = await ctx.db
@@ -76,7 +76,10 @@ export const listarSolicitacoesSubordinados = query({
.filter((q) => q.eq(q.field("ativo"), true))
.collect();
const solicitacoes: Array<any> = [];
const solicitacoes: Array<Doc<"solicitacoesFerias"> & {
funcionario: Doc<"funcionarios"> | null;
time: Doc<"times"> | null;
}> = [];
for (const time of timesGestor) {
// Buscar membros do time
@@ -113,9 +116,9 @@ export const listarSolicitacoesSubordinados = query({
});
// Query: Obter detalhes completos de uma solicitação
// Retorna tipo inferido automaticamente pelo Convex
export const obterDetalhes = query({
args: { solicitacaoId: v.id("solicitacoesFerias") },
returns: v.union(v.any(), v.null()),
handler: async (ctx, args) => {
const solicitacao = await ctx.db.get(args.solicitacaoId);
if (!solicitacao) return null;

View File

@@ -71,7 +71,7 @@ export const getAll = query({
export const getById = query({
args: { id: v.id("funcionarios") },
returns: v.union(v.any(), v.null()),
// Tipo inferido automaticamente pelo Convex
handler: async (ctx, args) => {
// Autorização: ver funcionário
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
@@ -205,7 +205,7 @@ export const create = mutation({
}
}
const novoFuncionarioId = await ctx.db.insert("funcionarios", args as any);
const novoFuncionarioId = await ctx.db.insert("funcionarios", args);
return novoFuncionarioId;
},
});
@@ -335,7 +335,7 @@ export const update = mutation({
}
const { id, ...updateData } = args;
await ctx.db.patch(id, updateData as any);
await ctx.db.patch(id, updateData);
return null;
},
});
@@ -358,7 +358,7 @@ export const remove = mutation({
// Query para obter ficha completa para impressão
export const getFichaCompleta = query({
args: { id: v.id("funcionarios") },
returns: v.union(v.any(), v.null()),
// Tipo inferido automaticamente pelo Convex
handler: async (ctx, args) => {
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
recurso: "funcionarios",
@@ -398,11 +398,10 @@ export const getFichaCompleta = query({
? {
nome: simbolo.nome,
descricao: simbolo.descricao,
// campos adicionais, se existirem no símbolo
tipo: (simbolo as any).tipo,
vencValor: (simbolo as any).vencValor,
repValor: (simbolo as any).repValor,
valor: (simbolo as any).valor,
tipo: simbolo.tipo,
vencValor: simbolo.vencValor,
repValor: simbolo.repValor,
valor: simbolo.valor,
}
: null,
cursos: cursosComUrls,

View File

@@ -38,7 +38,7 @@ export const executar = internalMutation({
}
// 2. Agrupar funcionários por gestor
const gestoresMap = new Map<string, any[]>();
const gestoresMap = new Map<string, Doc<"funcionarios">[]>();
for (const funcionario of funcionariosComGestor) {
if (!funcionario.gestorId) continue;
@@ -53,7 +53,7 @@ export const executar = internalMutation({
// 3. Para cada gestor, criar um time
for (const [gestorId, subordinados] of gestoresMap.entries()) {
try {
const gestor = await ctx.db.get(gestorId as any);
const gestor = await ctx.db.get(gestorId);
if (!gestor) {
erros.push(`Gestor ${gestorId} não encontrado`);
@@ -63,7 +63,7 @@ export const executar = internalMutation({
// Verificar se já existe time para este gestor
const timeExistente = await ctx.db
.query("times")
.withIndex("by_gestor", (q) => q.eq("gestorId", gestorId as any))
.withIndex("by_gestor", (q) => q.eq("gestorId", gestorId))
.filter((q) => q.eq(q.field("ativo"), true))
.first();
@@ -76,7 +76,7 @@ export const executar = internalMutation({
timeId = await ctx.db.insert("times", {
nome: `Equipe ${gestor.nome}`,
descricao: `Time gerenciado por ${gestor.nome} (migração automática)`,
gestorId: gestorId as any,
gestorId: gestorId,
ativo: true,
cor: "#3B82F6",
});
@@ -102,7 +102,7 @@ export const executar = internalMutation({
});
funcionariosAtribuidos++;
}
} catch (e: any) {
} catch (e) {
erros.push(`Erro ao adicionar ${funcionario.nome} ao time: ${e.message}`);
}
}

View File

@@ -1,15 +1,16 @@
import { v } from "convex/values";
import { mutation, query, internalMutation } from "./_generated/server";
import { internal } from "./_generated/api";
import { Id } from "./_generated/dataModel";
import { Id, Doc } from "./_generated/dataModel";
import type { QueryCtx } from "./_generated/server";
/**
* Helper para obter usuário autenticado
*/
async function getUsuarioAutenticado(ctx: any) {
async function getUsuarioAutenticado(ctx: QueryCtx) {
const usuariosOnline = await ctx.db.query("usuarios").collect();
const usuarioOnline = usuariosOnline.find(
(u: any) => u.statusPresenca === "online"
(u) => u.statusPresenca === "online"
);
return usuarioOnline || null;
}
@@ -298,7 +299,7 @@ export const verificarAlertasInternal = internalMutation({
for (const alerta of alertasAtivos) {
// Obter valor da métrica correspondente
const metricValue = (metrica as any)[alerta.metricName];
const metricValue = (metrica as Record<string, number>)[alerta.metricName];
if (metricValue === undefined) continue;
@@ -355,7 +356,7 @@ export const verificarAlertasInternal = internalMutation({
// Buscar usuários TI para notificar
const usuarios = await ctx.db.query("usuarios").collect();
const usuariosTI = usuarios.filter(
(u: any) => u.role?.nome === "ti" || u.role?.nivel === 0
(u) => u.role?.nome === "ti" || u.role?.nivel === 0
);
for (const usuario of usuariosTI) {

View File

@@ -9,7 +9,7 @@ import { Id } from "./_generated/dataModel";
*/
export const listarPerfisCustomizados = query({
args: {},
returns: v.array(v.any()),
// Tipo inferido automaticamente pelo Convex
handler: async (ctx) => {
const perfis = await ctx.db.query("perfisCustomizados").collect();
@@ -47,11 +47,11 @@ export const obterPerfilComPermissoes = query({
},
returns: v.union(
v.object({
perfil: v.any(),
role: v.any(),
permissoes: v.array(v.any()),
menuPermissoes: v.array(v.any()),
usuarios: v.array(v.any()),
perfil: v.any(), // Doc<"perfisCustomizados"> não pode ser validado diretamente
role: v.any(), // Doc<"roles"> não pode ser validado diretamente
permissoes: v.array(v.any()), // Doc<"permissoes">[] não pode ser validado diretamente
menuPermissoes: v.array(v.any()), // Doc<"menuPermissoes">[] não pode ser validado diretamente
usuarios: v.array(v.any()), // Doc<"usuarios">[] não pode ser validado diretamente
}),
v.null()
),
@@ -227,7 +227,7 @@ export const editarPerfilCustomizado = mutation({
}
// Atualizar perfil
const updates: any = {
const updates: Partial<Doc<"perfisCustomizados">> & { atualizadoEm: number } = {
atualizadoEm: Date.now(),
};
@@ -419,7 +419,7 @@ export const clonarPerfil = mutation({
// Log de atividade
await registrarAtividade(
ctx as any,
ctx,
args.criadoPorId,
"criar",
"perfis",

View File

@@ -165,7 +165,7 @@ export const assertPermissaoAcaoAtual = internalQuery({
returns: v.null(),
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
let usuarioAtual: any = null;
let usuarioAtual: Doc<"usuarios"> | null = null;
if (identity && identity.email) {
usuarioAtual = await ctx.db
@@ -187,9 +187,9 @@ export const assertPermissaoAcaoAtual = internalQuery({
if (!usuarioAtual) throw new Error("acesso_negado");
const role: any = await ctx.db.get(usuarioAtual.roleId as any);
const role = await ctx.db.get(usuarioAtual.roleId);
if (!role) throw new Error("acesso_negado");
if ((role as any).nivel <= 1) return null;
if (role.nivel <= 1) return null;
const permissao = await ctx.db
.query("permissoes")
@@ -201,7 +201,7 @@ export const assertPermissaoAcaoAtual = internalQuery({
const links = await ctx.db
.query("rolePermissoes")
.withIndex("by_role", (q) => q.eq("roleId", (role as any)._id as any))
.withIndex("by_role", (q) => q.eq("roleId", role._id))
.collect();
const ok = links.some((rp) => rp.permissaoId === permissao!._id);
if (!ok) throw new Error("acesso_negado");

View File

@@ -87,7 +87,7 @@ function calcularDataFimPeriodo(dataAdmissao: string, anosPassados: number): str
}
// Helper: Obter regime de trabalho do funcionário
async function obterRegimeTrabalho(ctx: any, funcionarioId: Id<"funcionarios">): Promise<RegimeTrabalho> {
async function obterRegimeTrabalho(ctx: QueryCtx, funcionarioId: Id<"funcionarios">): Promise<RegimeTrabalho> {
const funcionario = await ctx.db.get(funcionarioId);
return funcionario?.regimeTrabalho || "clt"; // Default CLT
}

View File

@@ -1,6 +1,7 @@
import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
import { registrarAtividade } from "./logsAtividades";
import { Doc } from "./_generated/dataModel";
/**
* Listar todos os templates
@@ -111,7 +112,7 @@ export const editarTemplate = mutation({
}
// Atualizar template
const updates: any = {};
const updates: Partial<Doc<"templatesMensagens">> = {};
if (args.nome !== undefined) updates.nome = args.nome;
if (args.titulo !== undefined) updates.titulo = args.titulo;
if (args.corpo !== undefined) updates.corpo = args.corpo;

View File

@@ -1,11 +1,11 @@
import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
import { Id } from "./_generated/dataModel";
import { Id, Doc } from "./_generated/dataModel";
// Query: Listar todos os times
// Tipo inferido automaticamente pelo Convex
export const listar = query({
args: {},
returns: v.array(v.any()),
handler: async (ctx) => {
const times = await ctx.db.query("times").collect();
@@ -31,9 +31,9 @@ export const listar = query({
});
// Query: Obter time por ID com membros
// Tipo inferido automaticamente pelo Convex
export const obterPorId = query({
args: { id: v.id("times") },
returns: v.union(v.any(), v.null()),
handler: async (ctx, args) => {
const time = await ctx.db.get(args.id);
if (!time) return null;
@@ -64,9 +64,9 @@ export const obterPorId = query({
});
// Query: Obter time do funcionário
// Tipo inferido automaticamente pelo Convex
export const obterTimeFuncionario = query({
args: { funcionarioId: v.id("funcionarios") },
returns: v.union(v.any(), v.null()),
handler: async (ctx, args) => {
const relacao = await ctx.db
.query("timesMembros")
@@ -89,9 +89,9 @@ export const obterTimeFuncionario = query({
});
// Query: Obter times do gestor
// Tipo inferido automaticamente pelo Convex
export const listarPorGestor = query({
args: { gestorId: v.id("usuarios") },
returns: v.array(v.any()),
handler: async (ctx, args) => {
const times = await ctx.db
.query("times")

View File

@@ -11,15 +11,22 @@
"jsx": "react-jsx",
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"types": [],
/* These compiler options are required by Convex */
"target": "ESNext",
"lib": ["ES2021", "dom"],
"lib": [
"ES2021",
"dom"
],
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"isolatedModules": true,
"noEmit": true
},
"include": ["./**/*"],
"exclude": ["./_generated"]
}
"include": [
"./**/*"
],
"exclude": [
"./_generated"
]
}

View File

@@ -2,7 +2,7 @@ import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
import { hashPassword, generateToken } from "./auth/utils";
import { registrarAtividade } from "./logsAtividades";
import { Id } from "./_generated/dataModel";
import { Id, Doc } from "./_generated/dataModel";
import { api } from "./_generated/api";
/**
@@ -436,7 +436,7 @@ export const atualizarPerfil = mutation({
}
// Atualizar apenas os campos fornecidos
const updates: any = { 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;
@@ -882,7 +882,7 @@ export const editarUsuario = mutation({
}
// Atualizar campos fornecidos
const updates: any = {
const updates: Partial<Doc<"usuarios">> & { atualizadoEm: number } = {
atualizadoEm: Date.now(),
};

View File

@@ -1,5 +1,6 @@
import { internalMutation, query } from "./_generated/server";
import { v } from "convex/values";
import { Id, Doc } from "./_generated/dataModel";
/**
* Verificar duplicatas de matrícula
@@ -33,7 +34,7 @@ export const verificarDuplicatas = query({
email: usuario.email || "",
});
return acc;
}, {} as Record<string, any[]>);
}, {} as Record<string, Array<{ _id: Id<"usuarios">; nome: string; email: string }>>);
// Filtrar apenas duplicatas
const duplicatas = Object.entries(gruposPorMatricula)
@@ -67,7 +68,7 @@ export const removerDuplicatas = internalMutation({
}
acc[usuario.matricula].push(usuario);
return acc;
}, {} as Record<string, any[]>);
}, {} as Record<string, Doc<"usuarios">[]>);
let removidos = 0;
const matriculasDuplicadas: string[] = [];