Merge branch 'master' into feat-controle-ponto
This commit is contained in:
6
packages/backend/convex/_generated/api.d.ts
vendored
6
packages/backend/convex/_generated/api.d.ts
vendored
@@ -23,12 +23,14 @@ import type * as chat from "../chat.js";
|
||||
import type * as configuracaoEmail from "../configuracaoEmail.js";
|
||||
import type * as configuracaoPonto from "../configuracaoPonto.js";
|
||||
import type * as configuracaoRelogio from "../configuracaoRelogio.js";
|
||||
import type * as contratos from "../contratos.js";
|
||||
import type * as crons from "../crons.js";
|
||||
import type * as cursos from "../cursos.js";
|
||||
import type * as dashboard from "../dashboard.js";
|
||||
import type * as documentos from "../documentos.js";
|
||||
import type * as email from "../email.js";
|
||||
import type * as enderecosMarcacao from "../enderecosMarcacao.js";
|
||||
import type * as empresas from "../empresas.js";
|
||||
import type * as ferias from "../ferias.js";
|
||||
import type * as funcionarioEnderecos from "../funcionarioEnderecos.js";
|
||||
import type * as funcionarios from "../funcionarios.js";
|
||||
@@ -47,7 +49,6 @@ import type * as saldoFerias from "../saldoFerias.js";
|
||||
import type * as security from "../security.js";
|
||||
import type * as seed from "../seed.js";
|
||||
import type * as simbolos from "../simbolos.js";
|
||||
import type * as solicitacoesAcesso from "../solicitacoesAcesso.js";
|
||||
import type * as templatesMensagens from "../templatesMensagens.js";
|
||||
import type * as times from "../times.js";
|
||||
import type * as todos from "../todos.js";
|
||||
@@ -77,12 +78,14 @@ declare const fullApi: ApiFromModules<{
|
||||
configuracaoEmail: typeof configuracaoEmail;
|
||||
configuracaoPonto: typeof configuracaoPonto;
|
||||
configuracaoRelogio: typeof configuracaoRelogio;
|
||||
contratos: typeof contratos;
|
||||
crons: typeof crons;
|
||||
cursos: typeof cursos;
|
||||
dashboard: typeof dashboard;
|
||||
documentos: typeof documentos;
|
||||
email: typeof email;
|
||||
enderecosMarcacao: typeof enderecosMarcacao;
|
||||
empresas: typeof empresas;
|
||||
ferias: typeof ferias;
|
||||
funcionarioEnderecos: typeof funcionarioEnderecos;
|
||||
funcionarios: typeof funcionarios;
|
||||
@@ -101,7 +104,6 @@ declare const fullApi: ApiFromModules<{
|
||||
security: typeof security;
|
||||
seed: typeof seed;
|
||||
simbolos: typeof simbolos;
|
||||
solicitacoesAcesso: typeof solicitacoesAcesso;
|
||||
templatesMensagens: typeof templatesMensagens;
|
||||
times: typeof times;
|
||||
todos: typeof todos;
|
||||
|
||||
@@ -70,8 +70,7 @@ export const criarConversa = mutation({
|
||||
args: {
|
||||
tipo: v.union(v.literal('individual'), v.literal('grupo'), v.literal('sala_reuniao')),
|
||||
participantes: v.array(v.id('usuarios')),
|
||||
nome: v.optional(v.string()),
|
||||
avatar: v.optional(v.string())
|
||||
nome: v.optional(v.string())
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const usuarioAtual = await getUsuarioAutenticado(ctx);
|
||||
@@ -103,7 +102,6 @@ export const criarConversa = mutation({
|
||||
const dadosConversa: Omit<Doc<'conversas'>, '_id' | '_creationTime'> = {
|
||||
tipo: args.tipo,
|
||||
nome: args.nome,
|
||||
avatar: args.avatar,
|
||||
participantes: args.participantes,
|
||||
criadoPor: usuarioAtual._id,
|
||||
criadoEm: Date.now()
|
||||
@@ -152,8 +150,7 @@ export const criarConversa = mutation({
|
||||
export const criarSalaReuniao = mutation({
|
||||
args: {
|
||||
nome: v.string(),
|
||||
participantes: v.array(v.id('usuarios')),
|
||||
avatar: v.optional(v.string())
|
||||
participantes: v.array(v.id('usuarios'))
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const usuarioAtual = await getUsuarioAutenticado(ctx);
|
||||
@@ -174,7 +171,6 @@ export const criarSalaReuniao = mutation({
|
||||
const dadosConversa: Omit<Doc<'conversas'>, '_id' | '_creationTime'> = {
|
||||
tipo: 'sala_reuniao' as const,
|
||||
nome: args.nome.trim(),
|
||||
avatar: args.avatar,
|
||||
participantes: participantesUnicos,
|
||||
criadoPor: usuarioAtual._id,
|
||||
criadoEm: Date.now(),
|
||||
@@ -2010,7 +2006,6 @@ export const obterUsuariosOnline = query({
|
||||
_id: u._id,
|
||||
nome: u.nome,
|
||||
email: u.email,
|
||||
avatar: u.avatar,
|
||||
fotoPerfil: u.fotoPerfil,
|
||||
statusPresenca: u.statusPresenca,
|
||||
statusMensagem: u.statusMensagem,
|
||||
@@ -2055,7 +2050,6 @@ export const listarTodosUsuarios = query({
|
||||
nome: u.nome,
|
||||
email: u.email,
|
||||
matricula,
|
||||
avatar: u.avatar,
|
||||
fotoPerfil: u.fotoPerfil,
|
||||
fotoPerfilUrl,
|
||||
statusPresenca: u.statusPresenca,
|
||||
|
||||
200
packages/backend/convex/contratos.ts
Normal file
200
packages/backend/convex/contratos.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
import { mutation, query } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
import { situacaoContrato } from "./schema";
|
||||
import { getCurrentUserFunction } from "./auth";
|
||||
import { internal } from "./_generated/api";
|
||||
|
||||
export const listar = query({
|
||||
args: {
|
||||
responsavelId: v.optional(v.id("funcionarios")),
|
||||
dataInicio: v.optional(v.string()),
|
||||
dataFim: v.optional(v.string()),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
recurso: "contratos",
|
||||
acao: "listar",
|
||||
});
|
||||
|
||||
let q = ctx.db.query("contratos");
|
||||
|
||||
if (args.responsavelId) {
|
||||
q = q.withIndex("by_responsavel", (q) =>
|
||||
q.eq("responsavelId", args.responsavelId!)
|
||||
) as typeof q;
|
||||
}
|
||||
|
||||
const contratos = await q.collect();
|
||||
|
||||
// Filtros em memória para datas (já que Convex não tem filtro de range nativo eficiente combinado com outros índices sem setup complexo)
|
||||
// Se o volume for muito grande, ideal seria criar índices específicos ou usar search.
|
||||
let resultado = contratos;
|
||||
|
||||
if (args.dataInicio) {
|
||||
resultado = resultado.filter(
|
||||
(c) => c.dataInicioVigencia >= args.dataInicio!
|
||||
);
|
||||
}
|
||||
|
||||
if (args.dataFim) {
|
||||
resultado = resultado.filter((c) => c.dataFimVigencia <= args.dataFim!);
|
||||
}
|
||||
|
||||
// Enriquecer com dados relacionados
|
||||
const contratosEnriquecidos = await Promise.all(
|
||||
resultado.map(async (c) => {
|
||||
const contratada = await ctx.db.get(c.contratadaId);
|
||||
const responsavel = await ctx.db.get(c.responsavelId);
|
||||
return {
|
||||
...c,
|
||||
contratada,
|
||||
responsavel,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return contratosEnriquecidos;
|
||||
},
|
||||
});
|
||||
|
||||
export const obter = query({
|
||||
args: { id: v.id("contratos") },
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
recurso: "contratos",
|
||||
acao: "ver",
|
||||
});
|
||||
const contrato = await ctx.db.get(args.id);
|
||||
if (!contrato) return null;
|
||||
|
||||
const contratada = await ctx.db.get(contrato.contratadaId);
|
||||
const responsavel = await ctx.db.get(contrato.responsavelId);
|
||||
|
||||
return {
|
||||
...contrato,
|
||||
contratada,
|
||||
responsavel,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const criar = mutation({
|
||||
args: {
|
||||
contratadaId: v.id("empresas"),
|
||||
objeto: v.string(),
|
||||
numeroNotaEmpenho: v.string(),
|
||||
responsavelId: v.id("funcionarios"),
|
||||
departamento: v.string(),
|
||||
situacao: situacaoContrato,
|
||||
numeroProcessoLicitatorio: v.string(),
|
||||
modalidade: v.string(),
|
||||
numeroContrato: v.string(),
|
||||
anoContrato: v.number(),
|
||||
dataInicioVigencia: v.string(),
|
||||
dataFimVigencia: v.string(),
|
||||
nomeFiscal: v.string(),
|
||||
valorTotal: v.string(),
|
||||
dataAditivoPrazo: v.optional(v.string()),
|
||||
diasAvisoVencimento: v.number(),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
recurso: "contratos",
|
||||
acao: "criar",
|
||||
});
|
||||
|
||||
const usuario = await getCurrentUserFunction(ctx);
|
||||
if (!usuario) throw new Error("Não autenticado");
|
||||
|
||||
const id = await ctx.db.insert("contratos", {
|
||||
...args,
|
||||
criadoPor: usuario._id,
|
||||
criadoEm: Date.now(),
|
||||
});
|
||||
|
||||
return id;
|
||||
},
|
||||
});
|
||||
|
||||
export const editar = mutation({
|
||||
args: {
|
||||
id: v.id("contratos"),
|
||||
contratadaId: v.optional(v.id("empresas")),
|
||||
objeto: v.optional(v.string()),
|
||||
numeroNotaEmpenho: v.optional(v.string()),
|
||||
responsavelId: v.optional(v.id("funcionarios")),
|
||||
departamento: v.optional(v.string()),
|
||||
situacao: v.optional(situacaoContrato),
|
||||
numeroProcessoLicitatorio: v.optional(v.string()),
|
||||
modalidade: v.optional(v.string()),
|
||||
numeroContrato: v.optional(v.string()),
|
||||
anoContrato: v.optional(v.number()),
|
||||
dataInicioVigencia: v.optional(v.string()),
|
||||
dataFimVigencia: v.optional(v.string()),
|
||||
nomeFiscal: v.optional(v.string()),
|
||||
valorTotal: v.optional(v.string()),
|
||||
dataAditivoPrazo: v.optional(v.string()),
|
||||
diasAvisoVencimento: v.optional(v.number()),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
recurso: "contratos",
|
||||
acao: "editar",
|
||||
});
|
||||
|
||||
const { id, ...campos } = args;
|
||||
|
||||
await ctx.db.patch(id, {
|
||||
...campos,
|
||||
atualizadoEm: Date.now(),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const excluir = mutation({
|
||||
args: { id: v.id("contratos") },
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
recurso: "contratos",
|
||||
acao: "excluir",
|
||||
});
|
||||
await ctx.db.delete(args.id);
|
||||
},
|
||||
});
|
||||
|
||||
export const verificarVencimentos = query({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
// Esta query pode ser usada por um componente de notificação ou cron job
|
||||
// Retorna contratos que estão próximos do vencimento baseados no diasAvisoVencimento
|
||||
|
||||
const hoje = new Date();
|
||||
const hojeStr = hoje.toISOString().split("T")[0];
|
||||
|
||||
// Buscar contratos ativos (em execução ou aguardando assinatura)
|
||||
const contratos = await ctx.db
|
||||
.query("contratos")
|
||||
.filter((q) =>
|
||||
q.or(
|
||||
q.eq(q.field("situacao"), "em_execucao"),
|
||||
q.eq(q.field("situacao"), "aguardando_assinatura")
|
||||
)
|
||||
)
|
||||
.collect();
|
||||
|
||||
const proximosVencimento = contratos.filter((c) => {
|
||||
if (!c.dataFimVigencia) return false;
|
||||
|
||||
const dataFim = new Date(c.dataFimVigencia);
|
||||
const dataAviso = new Date(dataFim);
|
||||
dataAviso.setDate(dataAviso.getDate() - c.diasAvisoVencimento);
|
||||
|
||||
const dataAvisoStr = dataAviso.toISOString().split("T")[0];
|
||||
|
||||
// Se hoje for maior ou igual a data de aviso e menor que a data fim
|
||||
return hojeStr >= dataAvisoStr && hojeStr <= c.dataFimVigencia;
|
||||
});
|
||||
|
||||
return proximosVencimento;
|
||||
},
|
||||
});
|
||||
@@ -7,8 +7,6 @@ export const getStats = query({
|
||||
returns: v.object({
|
||||
totalFuncionarios: v.number(),
|
||||
totalSimbolos: v.number(),
|
||||
totalSolicitacoesAcesso: v.number(),
|
||||
solicitacoesPendentes: v.number(),
|
||||
funcionariosAtivos: v.number(),
|
||||
funcionariosDesligados: v.number(),
|
||||
cargoComissionado: v.number(),
|
||||
@@ -42,19 +40,9 @@ export const getStats = query({
|
||||
const simbolos = await ctx.db.query("simbolos").collect();
|
||||
const totalSimbolos = simbolos.length;
|
||||
|
||||
// Contar solicitações de acesso
|
||||
const solicitacoes = await ctx.db.query("solicitacoesAcesso").collect();
|
||||
const totalSolicitacoesAcesso = solicitacoes.length;
|
||||
|
||||
const solicitacoesPendentes = solicitacoes.filter(
|
||||
(s) => s.status === "pendente"
|
||||
).length;
|
||||
|
||||
return {
|
||||
totalFuncionarios,
|
||||
totalSimbolos,
|
||||
totalSolicitacoesAcesso,
|
||||
solicitacoesPendentes,
|
||||
funcionariosAtivos,
|
||||
funcionariosDesligados,
|
||||
cargoComissionado,
|
||||
@@ -68,7 +56,6 @@ export const getRecentActivity = query({
|
||||
args: {},
|
||||
returns: v.object({
|
||||
funcionariosCadastrados24h: v.number(),
|
||||
solicitacoesAcesso24h: v.number(),
|
||||
simbolosCadastrados24h: v.number(),
|
||||
}),
|
||||
handler: async (ctx) => {
|
||||
@@ -81,11 +68,6 @@ export const getRecentActivity = query({
|
||||
(f) => f._creationTime >= last24h
|
||||
).length;
|
||||
|
||||
// Solicitações de acesso nas últimas 24h
|
||||
const solicitacoes = await ctx.db.query("solicitacoesAcesso").collect();
|
||||
const solicitacoesAcesso24h = solicitacoes.filter(
|
||||
(s) => s.dataSolicitacao >= last24h
|
||||
).length;
|
||||
|
||||
// Símbolos cadastrados nas últimas 24h
|
||||
const simbolos = await ctx.db.query("simbolos").collect();
|
||||
@@ -95,7 +77,6 @@ export const getRecentActivity = query({
|
||||
|
||||
return {
|
||||
funcionariosCadastrados24h,
|
||||
solicitacoesAcesso24h,
|
||||
simbolosCadastrados24h,
|
||||
};
|
||||
},
|
||||
@@ -137,15 +118,13 @@ export const getEvolucaoCadastros = query({
|
||||
v.object({
|
||||
mes: v.string(),
|
||||
funcionarios: v.number(),
|
||||
solicitacoes: v.number(),
|
||||
})
|
||||
),
|
||||
handler: async (ctx) => {
|
||||
const funcionarios = await ctx.db.query("funcionarios").collect();
|
||||
const solicitacoes = await ctx.db.query("solicitacoesAcesso").collect();
|
||||
|
||||
const now = new Date();
|
||||
const meses: Array<{ mes: string; funcionarios: number; solicitacoes: number }> = [];
|
||||
const meses: Array<{ mes: string; funcionarios: number }> = [];
|
||||
|
||||
// Últimos 6 meses
|
||||
for (let i = 5; i >= 0; i--) {
|
||||
@@ -161,14 +140,9 @@ export const getEvolucaoCadastros = query({
|
||||
(f) => f._creationTime >= date.getTime() && f._creationTime < nextDate.getTime()
|
||||
).length;
|
||||
|
||||
const solCount = solicitacoes.filter(
|
||||
(s) => s.dataSolicitacao >= date.getTime() && s.dataSolicitacao < nextDate.getTime()
|
||||
).length;
|
||||
|
||||
meses.push({
|
||||
mes: mesNome,
|
||||
funcionarios: funcCount,
|
||||
solicitacoes: solCount,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +162,29 @@ export const enfileirarEmail = mutation({
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Cancelar agendamento de email
|
||||
*/
|
||||
export const cancelarAgendamentoEmail = mutation({
|
||||
args: {
|
||||
emailId: v.id("notificacoesEmail"),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const email = await ctx.db.get(args.emailId);
|
||||
if (!email) {
|
||||
return { sucesso: false, erro: "Email não encontrado" };
|
||||
}
|
||||
|
||||
if (email.status !== "pendente") {
|
||||
return { sucesso: false, erro: "Apenas emails pendentes podem ser cancelados" };
|
||||
}
|
||||
|
||||
// Remove o email da fila
|
||||
await ctx.db.delete(args.emailId);
|
||||
return { sucesso: true };
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Enviar email usando template
|
||||
*/
|
||||
|
||||
292
packages/backend/convex/empresas.ts
Normal file
292
packages/backend/convex/empresas.ts
Normal file
@@ -0,0 +1,292 @@
|
||||
import { v } from "convex/values";
|
||||
import { mutation, query } from "./_generated/server";
|
||||
import { internal } from "./_generated/api";
|
||||
import { getCurrentUserFunction } from "./auth";
|
||||
import type { Id } from "./_generated/dataModel";
|
||||
|
||||
export const list = query({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
recurso: "empresas",
|
||||
acao: "listar",
|
||||
});
|
||||
|
||||
const empresas = await ctx.db.query("empresas").collect();
|
||||
return empresas;
|
||||
},
|
||||
});
|
||||
|
||||
export const getById = query({
|
||||
args: { id: v.id("empresas") },
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
recurso: "empresas",
|
||||
acao: "ver",
|
||||
});
|
||||
|
||||
const empresa = await ctx.db.get(args.id);
|
||||
if (!empresa) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const contatos = await ctx.db
|
||||
.query("contatosEmpresa")
|
||||
.withIndex("by_empresa", (q) => q.eq("empresaId", args.id))
|
||||
.collect();
|
||||
const endereco = empresa.enderecoId
|
||||
? await ctx.db.get(empresa.enderecoId as Id<"enderecos">)
|
||||
: null;
|
||||
|
||||
return { ...empresa, endereco, contatos };
|
||||
},
|
||||
});
|
||||
|
||||
const contatoInput = v.object({
|
||||
_id: v.optional(v.id("contatosEmpresa")),
|
||||
empresaId: v.optional(v.id("empresas")),
|
||||
nome: v.string(),
|
||||
funcao: v.string(),
|
||||
email: v.string(),
|
||||
telefone: v.string(),
|
||||
adicionadoPor: v.optional(v.id("usuarios")),
|
||||
descricao: v.optional(v.string()),
|
||||
_deleted: v.optional(v.boolean()),
|
||||
});
|
||||
|
||||
const enderecoInput = v.object({
|
||||
cep: v.string(),
|
||||
logradouro: v.string(),
|
||||
numero: v.string(),
|
||||
complemento: v.optional(v.string()),
|
||||
bairro: v.string(),
|
||||
cidade: v.string(),
|
||||
uf: v.string(),
|
||||
});
|
||||
|
||||
export const create = mutation({
|
||||
args: {
|
||||
razao_social: v.string(),
|
||||
nome_fantasia: v.optional(v.string()),
|
||||
cnpj: v.string(),
|
||||
telefone: v.string(),
|
||||
email: v.string(),
|
||||
descricao: v.optional(v.string()),
|
||||
endereco: v.optional(enderecoInput),
|
||||
contatos: v.optional(v.array(contatoInput)),
|
||||
},
|
||||
returns: v.id("empresas"),
|
||||
handler: async (ctx, args) => {
|
||||
const usuarioAtual = await getCurrentUserFunction(ctx);
|
||||
|
||||
if (!usuarioAtual) {
|
||||
throw new Error("Usuário não autenticado.");
|
||||
}
|
||||
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
recurso: "empresas",
|
||||
acao: "criar",
|
||||
});
|
||||
|
||||
const cnpjExistente = await ctx.db
|
||||
.query("empresas")
|
||||
.withIndex("by_cnpj", (q) => q.eq("cnpj", args.cnpj))
|
||||
.unique();
|
||||
|
||||
if (cnpjExistente) {
|
||||
throw new Error("Já existe uma empresa cadastrada com este CNPJ.");
|
||||
}
|
||||
let enderecoId: Id<"enderecos"> | undefined;
|
||||
if (args.endereco) {
|
||||
enderecoId = await ctx.db.insert("enderecos", {
|
||||
cep: args.endereco.cep,
|
||||
logradouro: args.endereco.logradouro,
|
||||
numero: args.endereco.numero,
|
||||
complemento: args.endereco.complemento,
|
||||
bairro: args.endereco.bairro,
|
||||
cidade: args.endereco.cidade,
|
||||
uf: args.endereco.uf,
|
||||
criadoPor: usuarioAtual._id,
|
||||
atualizadoPor: usuarioAtual._id,
|
||||
});
|
||||
}
|
||||
|
||||
const empresaDoc: {
|
||||
razao_social: string;
|
||||
nome_fantasia?: string;
|
||||
cnpj: string;
|
||||
telefone: string;
|
||||
email: string;
|
||||
descricao?: string;
|
||||
enderecoId?: Id<"enderecos">;
|
||||
criadoPor: Id<"usuarios">;
|
||||
} = {
|
||||
razao_social: args.razao_social,
|
||||
cnpj: args.cnpj,
|
||||
telefone: args.telefone,
|
||||
email: args.email,
|
||||
criadoPor: usuarioAtual._id,
|
||||
};
|
||||
|
||||
if (args.nome_fantasia !== undefined) {
|
||||
empresaDoc.nome_fantasia = args.nome_fantasia;
|
||||
}
|
||||
if (args.descricao !== undefined) {
|
||||
empresaDoc.descricao = args.descricao;
|
||||
}
|
||||
if (enderecoId) {
|
||||
empresaDoc.enderecoId = enderecoId;
|
||||
}
|
||||
|
||||
const empresaId = await ctx.db.insert("empresas", empresaDoc);
|
||||
|
||||
if (args.contatos && args.contatos.length > 0) {
|
||||
for (const contato of args.contatos) {
|
||||
await ctx.db.insert("contatosEmpresa", {
|
||||
empresaId,
|
||||
nome: contato.nome,
|
||||
funcao: contato.funcao,
|
||||
email: contato.email,
|
||||
telefone: contato.telefone,
|
||||
adicionadoPor: usuarioAtual._id,
|
||||
descricao: contato.descricao,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return empresaId;
|
||||
},
|
||||
});
|
||||
|
||||
export const update = mutation({
|
||||
args: {
|
||||
id: v.id("empresas"),
|
||||
razao_social: v.string(),
|
||||
nome_fantasia: v.optional(v.string()),
|
||||
cnpj: v.string(),
|
||||
telefone: v.string(),
|
||||
email: v.string(),
|
||||
descricao: v.optional(v.string()),
|
||||
endereco: v.optional(enderecoInput),
|
||||
contatos: v.optional(v.array(contatoInput)),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
recurso: "empresas",
|
||||
acao: "editar",
|
||||
});
|
||||
|
||||
const cnpjExistente = await ctx.db
|
||||
.query("empresas")
|
||||
.withIndex("by_cnpj", (q) => q.eq("cnpj", args.cnpj))
|
||||
.unique();
|
||||
|
||||
if (cnpjExistente && cnpjExistente._id !== args.id) {
|
||||
throw new Error("Já existe uma empresa cadastrada com este CNPJ.");
|
||||
}
|
||||
const empresa = await ctx.db.get(args.id);
|
||||
if (!empresa) {
|
||||
throw new Error("Empresa não encontrada.");
|
||||
}
|
||||
|
||||
if (args.endereco) {
|
||||
if (empresa.enderecoId) {
|
||||
const usuarioAtual = await getCurrentUserFunction(ctx);
|
||||
await ctx.db.patch(empresa.enderecoId as Id<"enderecos">, {
|
||||
cep: args.endereco.cep,
|
||||
logradouro: args.endereco.logradouro,
|
||||
numero: args.endereco.numero,
|
||||
complemento: args.endereco.complemento,
|
||||
bairro: args.endereco.bairro,
|
||||
cidade: args.endereco.cidade,
|
||||
uf: args.endereco.uf,
|
||||
atualizadoPor: usuarioAtual?._id,
|
||||
});
|
||||
} else {
|
||||
const usuarioAtual = await getCurrentUserFunction(ctx);
|
||||
|
||||
if (!usuarioAtual) {
|
||||
throw new Error("Usuário não autenticado.");
|
||||
}
|
||||
|
||||
const novoEnderecoId: Id<"enderecos"> = await ctx.db.insert("enderecos", {
|
||||
cep: args.endereco.cep,
|
||||
logradouro: args.endereco.logradouro,
|
||||
numero: args.endereco.numero,
|
||||
complemento: args.endereco.complemento,
|
||||
bairro: args.endereco.bairro,
|
||||
cidade: args.endereco.cidade,
|
||||
uf: args.endereco.uf,
|
||||
criadoPor: usuarioAtual._id,
|
||||
atualizadoPor: usuarioAtual._id,
|
||||
});
|
||||
|
||||
await ctx.db.patch(args.id, {
|
||||
enderecoId: novoEnderecoId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const patchDoc: {
|
||||
razao_social: string;
|
||||
nome_fantasia?: string;
|
||||
cnpj: string;
|
||||
telefone: string;
|
||||
email: string;
|
||||
descricao?: string;
|
||||
} = {
|
||||
razao_social: args.razao_social,
|
||||
cnpj: args.cnpj,
|
||||
telefone: args.telefone,
|
||||
email: args.email,
|
||||
};
|
||||
|
||||
if (args.nome_fantasia !== undefined) {
|
||||
patchDoc.nome_fantasia = args.nome_fantasia;
|
||||
}
|
||||
if (args.descricao !== undefined) {
|
||||
patchDoc.descricao = args.descricao;
|
||||
}
|
||||
|
||||
await ctx.db.patch(args.id, patchDoc);
|
||||
|
||||
if (!args.contatos) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const contato of args.contatos) {
|
||||
if (contato._id && contato._deleted) {
|
||||
await ctx.db.delete(contato._id);
|
||||
} else if (contato._id) {
|
||||
await ctx.db.patch(contato._id, {
|
||||
nome: contato.nome,
|
||||
funcao: contato.funcao,
|
||||
email: contato.email,
|
||||
telefone: contato.telefone,
|
||||
descricao: contato.descricao,
|
||||
});
|
||||
} else if (!contato._deleted) {
|
||||
const usuarioAtual = await getCurrentUserFunction(ctx);
|
||||
|
||||
if (!usuarioAtual) {
|
||||
throw new Error("Usuário não autenticado.");
|
||||
}
|
||||
|
||||
await ctx.db.insert("contatosEmpresa", {
|
||||
empresaId: args.id,
|
||||
nome: contato.nome,
|
||||
funcao: contato.funcao,
|
||||
email: contato.email,
|
||||
telefone: contato.telefone,
|
||||
adicionadoPor: usuarioAtual._id,
|
||||
descricao: contato.descricao,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -594,12 +594,11 @@ export const getStatusSistema = query({
|
||||
}
|
||||
|
||||
// Total de registros (estimativa baseada em tabelas principais)
|
||||
const [usuarios, funcionarios, simbolos, solicitacoesAcesso, alertas, metricas] =
|
||||
const [usuarios, funcionarios, simbolos, alertas, metricas] =
|
||||
await Promise.all([
|
||||
ctx.db.query('usuarios').collect(),
|
||||
ctx.db.query('funcionarios').collect(),
|
||||
ctx.db.query('simbolos').collect(),
|
||||
ctx.db.query('solicitacoesAcesso').collect(),
|
||||
ctx.db.query('alertConfigurations').collect(),
|
||||
ctx.db.query('systemMetrics').take(100) // não precisa contar tudo
|
||||
]);
|
||||
@@ -607,7 +606,6 @@ export const getStatusSistema = query({
|
||||
usuarios.length +
|
||||
funcionarios.length +
|
||||
simbolos.length +
|
||||
solicitacoesAcesso.length +
|
||||
alertas.length +
|
||||
metricas.length;
|
||||
|
||||
|
||||
@@ -224,6 +224,36 @@ const PERMISSOES_BASE = {
|
||||
acao: 'ver',
|
||||
descricao: 'Acessar telas do módulo de licitações'
|
||||
},
|
||||
{
|
||||
nome: 'contratos.listar',
|
||||
recurso: 'contratos',
|
||||
acao: 'listar',
|
||||
descricao: 'Listar contratos'
|
||||
},
|
||||
{
|
||||
nome: 'contratos.criar',
|
||||
recurso: 'contratos',
|
||||
acao: 'criar',
|
||||
descricao: 'Criar novos contratos'
|
||||
},
|
||||
{
|
||||
nome: 'contratos.editar',
|
||||
recurso: 'contratos',
|
||||
acao: 'editar',
|
||||
descricao: 'Editar contratos'
|
||||
},
|
||||
{
|
||||
nome: 'contratos.excluir',
|
||||
recurso: 'contratos',
|
||||
acao: 'excluir',
|
||||
descricao: 'Excluir contratos'
|
||||
},
|
||||
{
|
||||
nome: 'contratos.ver',
|
||||
recurso: 'contratos',
|
||||
acao: 'ver',
|
||||
descricao: 'Visualizar detalhes de contratos'
|
||||
},
|
||||
// Compras
|
||||
{
|
||||
nome: 'compras.ver',
|
||||
|
||||
@@ -120,11 +120,78 @@ export const reportStatus = v.union(
|
||||
v.literal("falhou")
|
||||
);
|
||||
|
||||
export const situacaoContrato = v.union(
|
||||
v.literal("em_execucao"),
|
||||
v.literal("rescendido"),
|
||||
v.literal("aguardando_assinatura"),
|
||||
v.literal("finalizado")
|
||||
);
|
||||
|
||||
export default defineSchema({
|
||||
contratos: defineTable({
|
||||
contratadaId: v.id("empresas"),
|
||||
objeto: v.string(),
|
||||
numeroNotaEmpenho: v.string(),
|
||||
responsavelId: v.id("funcionarios"),
|
||||
departamento: v.string(),
|
||||
situacao: situacaoContrato,
|
||||
numeroProcessoLicitatorio: v.string(),
|
||||
modalidade: v.string(),
|
||||
numeroContrato: v.string(),
|
||||
anoContrato: v.number(),
|
||||
dataInicioVigencia: v.string(),
|
||||
dataFimVigencia: v.string(),
|
||||
nomeFiscal: v.string(),
|
||||
valorTotal: v.string(),
|
||||
dataAditivoPrazo: v.optional(v.string()),
|
||||
diasAvisoVencimento: v.number(),
|
||||
criadoPor: v.id("usuarios"),
|
||||
criadoEm: v.number(),
|
||||
atualizadoEm: v.optional(v.number()),
|
||||
})
|
||||
.index("by_responsavel", ["responsavelId"])
|
||||
.index("by_situacao", ["situacao"])
|
||||
.index("by_vigencia_inicio", ["dataInicioVigencia"])
|
||||
.index("by_vigencia_fim", ["dataFimVigencia"]),
|
||||
|
||||
todos: defineTable({
|
||||
text: v.string(),
|
||||
completed: v.boolean(),
|
||||
}),
|
||||
enderecos: defineTable({
|
||||
cep: v.string(),
|
||||
logradouro: v.string(),
|
||||
numero: v.string(),
|
||||
complemento: v.optional(v.string()),
|
||||
bairro: v.string(),
|
||||
cidade: v.string(),
|
||||
uf: v.string(),
|
||||
criadoPor: v.optional(v.id("usuarios")),
|
||||
atualizadoPor: v.optional(v.id("usuarios")),
|
||||
}).index("by_cep", ["cep"]),
|
||||
empresas: defineTable({
|
||||
razao_social: v.string(),
|
||||
nome_fantasia: v.optional(v.string()),
|
||||
cnpj: v.string(),
|
||||
telefone: v.string(),
|
||||
email: v.string(),
|
||||
descricao: v.optional(v.string()),
|
||||
enderecoId: v.optional(v.id("enderecos")),
|
||||
criadoPor: v.optional(v.id("usuarios")),
|
||||
})
|
||||
.index("by_razao_social", ["razao_social"])
|
||||
.index("by_cnpj", ["cnpj"]),
|
||||
contatosEmpresa: defineTable({
|
||||
empresaId: v.id("empresas"),
|
||||
nome: v.string(),
|
||||
funcao: v.string(),
|
||||
email: v.string(),
|
||||
telefone: v.string(),
|
||||
adicionadoPor: v.optional(v.id("usuarios")),
|
||||
descricao: v.optional(v.string()),
|
||||
})
|
||||
.index("by_empresa", ["empresaId"])
|
||||
.index("by_email", ["email"]),
|
||||
funcionarios: defineTable({
|
||||
// Campos obrigatórios existentes
|
||||
nome: v.string(),
|
||||
@@ -447,24 +514,6 @@ export default defineSchema({
|
||||
valor: v.string(),
|
||||
}),
|
||||
|
||||
solicitacoesAcesso: defineTable({
|
||||
nome: v.string(),
|
||||
matricula: v.string(),
|
||||
email: v.string(),
|
||||
telefone: v.string(),
|
||||
status: v.union(
|
||||
v.literal("pendente"),
|
||||
v.literal("aprovado"),
|
||||
v.literal("rejeitado")
|
||||
),
|
||||
dataSolicitacao: v.number(),
|
||||
dataResposta: v.optional(v.number()),
|
||||
observacoes: v.optional(v.string()),
|
||||
})
|
||||
.index("by_status", ["status"])
|
||||
.index("by_matricula", ["matricula"])
|
||||
.index("by_email", ["email"]),
|
||||
|
||||
// Sistema de Autenticação e Controle de Acesso
|
||||
usuarios: defineTable({
|
||||
authId: v.string(),
|
||||
@@ -486,7 +535,7 @@ export default defineSchema({
|
||||
ultimaTentativaLogin: v.optional(v.number()), // timestamp da última tentativa
|
||||
|
||||
// Campos de Chat e Perfil
|
||||
avatar: v.optional(v.string()), // "avatar-1" até "avatar-15" ou storageId
|
||||
|
||||
fotoPerfil: v.optional(v.id("_storage")),
|
||||
setor: v.optional(v.string()),
|
||||
statusMensagem: v.optional(v.string()), // max 100 chars
|
||||
@@ -712,7 +761,7 @@ export default defineSchema({
|
||||
v.literal("sala_reuniao")
|
||||
),
|
||||
nome: v.optional(v.string()), // nome do grupo/sala
|
||||
avatar: v.optional(v.string()), // avatar do grupo/sala
|
||||
|
||||
participantes: v.array(v.id("usuarios")), // IDs dos participantes
|
||||
administradores: v.optional(v.array(v.id("usuarios"))), // IDs dos administradores (apenas para sala_reuniao)
|
||||
ultimaMensagem: v.optional(v.string()),
|
||||
|
||||
@@ -164,27 +164,6 @@ const funcionariosData = [
|
||||
}
|
||||
];
|
||||
|
||||
const solicitacoesAcessoData = [
|
||||
{
|
||||
dataResposta: 1761445098933,
|
||||
dataSolicitacao: 1761445038329,
|
||||
email: 'severino@gmail.com',
|
||||
matricula: '3231',
|
||||
nome: 'Severino Gates',
|
||||
observacoes: 'Aprovação realizada por Deyvison',
|
||||
status: 'aprovado' as const,
|
||||
telefone: '(81) 9942-3551'
|
||||
},
|
||||
{
|
||||
dataSolicitacao: 1761445187258,
|
||||
email: 'michaeljackson@gmail.com',
|
||||
matricula: '123321',
|
||||
nome: 'Michael Jackson',
|
||||
status: 'pendente' as const,
|
||||
telefone: '(81) 99423-5551'
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Seed inicial do banco de dados com os dados exportados do Convex Cloud
|
||||
*/
|
||||
@@ -338,8 +317,6 @@ export const seedCreateUsuariosParaFuncionarios = internalMutation({
|
||||
});
|
||||
delay += 50;
|
||||
}
|
||||
// Agenda próxima etapa após as criações individuais
|
||||
await ctx.scheduler.runAfter(delay + 300, internal.seed.seedInserirSolicitacoesAcesso, {});
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@@ -402,55 +379,6 @@ export const seedCreateUsuarioParaFuncionario = internalMutation({
|
||||
}
|
||||
});
|
||||
|
||||
export const seedInserirSolicitacoesAcesso = internalMutation({
|
||||
args: {},
|
||||
returns: v.null(),
|
||||
handler: async (ctx) => {
|
||||
console.log('📋 Inserindo solicitações de acesso...');
|
||||
for (const solicitacao of solicitacoesAcessoData) {
|
||||
// Evitar duplicidade por matrícula
|
||||
const existente = await ctx.db
|
||||
.query('solicitacoesAcesso')
|
||||
.withIndex('by_matricula', (q) => q.eq('matricula', solicitacao.matricula))
|
||||
.first();
|
||||
if (existente) {
|
||||
console.log(` ℹ️ Solicitação já existe p/ matrícula ${solicitacao.matricula}`);
|
||||
continue;
|
||||
}
|
||||
const dadosSolicitacao: {
|
||||
nome: string;
|
||||
matricula: string;
|
||||
email: string;
|
||||
telefone: string;
|
||||
status: 'pendente' | 'aprovado' | 'rejeitado';
|
||||
dataSolicitacao: number;
|
||||
dataResposta?: number;
|
||||
observacoes?: string;
|
||||
} = {
|
||||
nome: solicitacao.nome,
|
||||
matricula: solicitacao.matricula,
|
||||
email: solicitacao.email,
|
||||
telefone: solicitacao.telefone,
|
||||
status: solicitacao.status,
|
||||
dataSolicitacao: solicitacao.dataSolicitacao
|
||||
};
|
||||
|
||||
if (solicitacao.dataResposta) {
|
||||
dadosSolicitacao.dataResposta = solicitacao.dataResposta;
|
||||
}
|
||||
|
||||
if (solicitacao.observacoes) {
|
||||
dadosSolicitacao.observacoes = solicitacao.observacoes;
|
||||
}
|
||||
|
||||
await ctx.db.insert('solicitacoesAcesso', dadosSolicitacao);
|
||||
console.log(` ✅ Solicitação criada: ${solicitacao.nome} (${solicitacao.status})`);
|
||||
}
|
||||
console.log('✨ Seed concluído!');
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
export const seedDatabase = internalAction({
|
||||
args: {},
|
||||
returns: v.null(),
|
||||
@@ -460,7 +388,6 @@ export const seedDatabase = internalAction({
|
||||
await ctx.runMutation(internal.seed.seedCreateSimbolos, {});
|
||||
await ctx.runMutation(internal.seed.seedCreateFuncionarios, {});
|
||||
await ctx.runMutation(internal.seed.seedCreateUsuariosParaFuncionarios, {});
|
||||
await ctx.runMutation(internal.seed.seedInserirSolicitacoesAcesso, {});
|
||||
console.log('✨ Seed do banco de dados concluído com sucesso pela action!');
|
||||
return null;
|
||||
}
|
||||
@@ -677,13 +604,6 @@ export const clearDatabase = internalMutation({
|
||||
}
|
||||
console.log(` ✅ ${funcionarios.length} funcionários removidos`);
|
||||
|
||||
// 20. Solicitações de acesso
|
||||
const solicitacoesAcesso = await ctx.db.query('solicitacoesAcesso').collect();
|
||||
for (const solicitacao of solicitacoesAcesso) {
|
||||
await ctx.db.delete(solicitacao._id);
|
||||
}
|
||||
console.log(` ✅ ${solicitacoesAcesso.length} solicitações de acesso removidas`);
|
||||
|
||||
// 21. Símbolos
|
||||
const simbolos = await ctx.db.query('simbolos').collect();
|
||||
for (const simbolo of simbolos) {
|
||||
@@ -907,13 +827,6 @@ export const limparBanco = mutation({
|
||||
}
|
||||
console.log(` ✅ ${funcionarios.length} funcionários removidos`);
|
||||
|
||||
// 20. Solicitações de acesso
|
||||
const solicitacoesAcesso = await ctx.db.query('solicitacoesAcesso').collect();
|
||||
for (const solicitacao of solicitacoesAcesso) {
|
||||
await ctx.db.delete(solicitacao._id);
|
||||
}
|
||||
console.log(` ✅ ${solicitacoesAcesso.length} solicitações de acesso removidas`);
|
||||
|
||||
// 21. Símbolos
|
||||
const simbolos = await ctx.db.query('simbolos').collect();
|
||||
for (const simbolo of simbolos) {
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
import { mutation, query } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
|
||||
// Criar uma nova solicitação de acesso
|
||||
export const create = mutation({
|
||||
args: {
|
||||
nome: v.string(),
|
||||
matricula: v.string(),
|
||||
email: v.string(),
|
||||
telefone: v.string(),
|
||||
},
|
||||
returns: v.object({
|
||||
solicitacaoId: v.id("solicitacoesAcesso"),
|
||||
}),
|
||||
handler: async (ctx, args) => {
|
||||
// Verificar se já existe uma solicitação pendente com a mesma matrícula
|
||||
const existingByMatricula = await ctx.db
|
||||
.query("solicitacoesAcesso")
|
||||
.withIndex("by_matricula", (q) => q.eq("matricula", args.matricula))
|
||||
.filter((q) => q.eq(q.field("status"), "pendente"))
|
||||
.first();
|
||||
|
||||
if (existingByMatricula) {
|
||||
throw new Error("Já existe uma solicitação pendente para esta matrícula.");
|
||||
}
|
||||
|
||||
// Verificar se já existe uma solicitação pendente com o mesmo email
|
||||
const existingByEmail = await ctx.db
|
||||
.query("solicitacoesAcesso")
|
||||
.withIndex("by_email", (q) => q.eq("email", args.email))
|
||||
.filter((q) => q.eq(q.field("status"), "pendente"))
|
||||
.first();
|
||||
|
||||
if (existingByEmail) {
|
||||
throw new Error("Já existe uma solicitação pendente para este e-mail.");
|
||||
}
|
||||
|
||||
const solicitacaoId = await ctx.db.insert("solicitacoesAcesso", {
|
||||
nome: args.nome,
|
||||
matricula: args.matricula,
|
||||
email: args.email,
|
||||
telefone: args.telefone,
|
||||
status: "pendente",
|
||||
dataSolicitacao: Date.now(),
|
||||
});
|
||||
|
||||
return { solicitacaoId };
|
||||
},
|
||||
});
|
||||
|
||||
// Listar todas as solicitações (para o painel administrativo)
|
||||
export const getAll = query({
|
||||
args: {},
|
||||
returns: v.array(
|
||||
v.object({
|
||||
_id: v.id("solicitacoesAcesso"),
|
||||
_creationTime: v.number(),
|
||||
nome: v.string(),
|
||||
matricula: v.string(),
|
||||
email: v.string(),
|
||||
telefone: v.string(),
|
||||
status: v.union(
|
||||
v.literal("pendente"),
|
||||
v.literal("aprovado"),
|
||||
v.literal("rejeitado")
|
||||
),
|
||||
dataSolicitacao: v.number(),
|
||||
dataResposta: v.union(v.number(), v.null()),
|
||||
observacoes: v.union(v.string(), v.null()),
|
||||
})
|
||||
),
|
||||
handler: async (ctx) => {
|
||||
const solicitacoes = await ctx.db
|
||||
.query("solicitacoesAcesso")
|
||||
.order("desc")
|
||||
.collect();
|
||||
|
||||
return solicitacoes.map((s) => ({
|
||||
_id: s._id,
|
||||
_creationTime: s._creationTime,
|
||||
nome: s.nome,
|
||||
matricula: s.matricula,
|
||||
email: s.email,
|
||||
telefone: s.telefone,
|
||||
status: s.status,
|
||||
dataSolicitacao: s.dataSolicitacao,
|
||||
dataResposta: s.dataResposta ?? null,
|
||||
observacoes: s.observacoes ?? null,
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
// Listar apenas solicitações pendentes
|
||||
export const getPendentes = query({
|
||||
args: {},
|
||||
returns: v.array(
|
||||
v.object({
|
||||
_id: v.id("solicitacoesAcesso"),
|
||||
_creationTime: v.number(),
|
||||
nome: v.string(),
|
||||
matricula: v.string(),
|
||||
email: v.string(),
|
||||
telefone: v.string(),
|
||||
status: v.union(
|
||||
v.literal("pendente"),
|
||||
v.literal("aprovado"),
|
||||
v.literal("rejeitado")
|
||||
),
|
||||
dataSolicitacao: v.number(),
|
||||
dataResposta: v.union(v.number(), v.null()),
|
||||
observacoes: v.union(v.string(), v.null()),
|
||||
})
|
||||
),
|
||||
handler: async (ctx) => {
|
||||
const solicitacoes = await ctx.db
|
||||
.query("solicitacoesAcesso")
|
||||
.withIndex("by_status", (q) => q.eq("status", "pendente"))
|
||||
.order("desc")
|
||||
.collect();
|
||||
|
||||
return solicitacoes.map((s) => ({
|
||||
_id: s._id,
|
||||
_creationTime: s._creationTime,
|
||||
nome: s.nome,
|
||||
matricula: s.matricula,
|
||||
email: s.email,
|
||||
telefone: s.telefone,
|
||||
status: s.status,
|
||||
dataSolicitacao: s.dataSolicitacao,
|
||||
dataResposta: s.dataResposta ?? null,
|
||||
observacoes: s.observacoes ?? null,
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
// Aprovar uma solicitação
|
||||
export const aprovar = mutation({
|
||||
args: {
|
||||
solicitacaoId: v.id("solicitacoesAcesso"),
|
||||
observacoes: v.optional(v.string()),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const solicitacao = await ctx.db.get(args.solicitacaoId);
|
||||
if (!solicitacao) {
|
||||
throw new Error("Solicitação não encontrada.");
|
||||
}
|
||||
|
||||
if (solicitacao.status !== "pendente") {
|
||||
throw new Error("Esta solicitação já foi processada.");
|
||||
}
|
||||
|
||||
await ctx.db.patch(args.solicitacaoId, {
|
||||
status: "aprovado",
|
||||
dataResposta: Date.now(),
|
||||
observacoes: args.observacoes,
|
||||
});
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
// Rejeitar uma solicitação
|
||||
export const rejeitar = mutation({
|
||||
args: {
|
||||
solicitacaoId: v.id("solicitacoesAcesso"),
|
||||
observacoes: v.optional(v.string()),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const solicitacao = await ctx.db.get(args.solicitacaoId);
|
||||
if (!solicitacao) {
|
||||
throw new Error("Solicitação não encontrada.");
|
||||
}
|
||||
|
||||
if (solicitacao.status !== "pendente") {
|
||||
throw new Error("Esta solicitação já foi processada.");
|
||||
}
|
||||
|
||||
await ctx.db.patch(args.solicitacaoId, {
|
||||
status: "rejeitado",
|
||||
dataResposta: Date.now(),
|
||||
observacoes: args.observacoes,
|
||||
});
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
// Obter uma solicitação por ID
|
||||
export const getById = query({
|
||||
args: {
|
||||
solicitacaoId: v.id("solicitacoesAcesso"),
|
||||
},
|
||||
returns: v.union(
|
||||
v.object({
|
||||
_id: v.id("solicitacoesAcesso"),
|
||||
_creationTime: v.number(),
|
||||
nome: v.string(),
|
||||
matricula: v.string(),
|
||||
email: v.string(),
|
||||
telefone: v.string(),
|
||||
status: v.union(
|
||||
v.literal("pendente"),
|
||||
v.literal("aprovado"),
|
||||
v.literal("rejeitado")
|
||||
),
|
||||
dataSolicitacao: v.number(),
|
||||
dataResposta: v.union(v.number(), v.null()),
|
||||
observacoes: v.union(v.string(), v.null()),
|
||||
}),
|
||||
v.null()
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
const solicitacao = await ctx.db.get(args.solicitacaoId);
|
||||
if (!solicitacao) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
_id: solicitacao._id,
|
||||
_creationTime: solicitacao._creationTime,
|
||||
nome: solicitacao.nome,
|
||||
matricula: solicitacao.matricula,
|
||||
email: solicitacao.email,
|
||||
telefone: solicitacao.telefone,
|
||||
status: solicitacao.status,
|
||||
dataSolicitacao: solicitacao.dataSolicitacao,
|
||||
dataResposta: solicitacao.dataResposta ?? null,
|
||||
observacoes: solicitacao.observacoes ?? null,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -476,11 +476,10 @@ export const alterarRole = mutation({
|
||||
});
|
||||
|
||||
/**
|
||||
* Atualizar perfil do usuário (foto, avatar, setor, status, preferências)
|
||||
* Atualizar perfil do usuário (foto, setor, status, preferências)
|
||||
*/
|
||||
export const atualizarPerfil = mutation({
|
||||
args: {
|
||||
avatar: v.optional(v.string()),
|
||||
fotoPerfil: v.optional(v.id('_storage')),
|
||||
setor: v.optional(v.string()),
|
||||
statusMensagem: v.optional(v.string()),
|
||||
@@ -511,7 +510,6 @@ export const atualizarPerfil = mutation({
|
||||
atualizadoEm: Date.now()
|
||||
};
|
||||
|
||||
if (args.avatar !== undefined) updates.avatar = args.avatar;
|
||||
if (args.fotoPerfil !== undefined) updates.fotoPerfil = args.fotoPerfil;
|
||||
if (args.setor !== undefined) updates.setor = args.setor;
|
||||
if (args.statusMensagem !== undefined) updates.statusMensagem = args.statusMensagem;
|
||||
@@ -541,7 +539,6 @@ export const obterPerfil = query({
|
||||
email: v.string(),
|
||||
matricula: v.optional(v.string()),
|
||||
funcionarioId: v.optional(v.id('funcionarios')),
|
||||
avatar: v.optional(v.string()),
|
||||
fotoPerfil: v.optional(v.id('_storage')),
|
||||
fotoPerfilUrl: v.union(v.string(), v.null()),
|
||||
setor: v.optional(v.string()),
|
||||
@@ -582,7 +579,6 @@ export const obterPerfil = query({
|
||||
email: usuarioAtual.email,
|
||||
matricula: matricula || undefined,
|
||||
funcionarioId: usuarioAtual.funcionarioId,
|
||||
avatar: usuarioAtual.avatar,
|
||||
fotoPerfil: usuarioAtual.fotoPerfil,
|
||||
fotoPerfilUrl,
|
||||
setor: usuarioAtual.setor,
|
||||
@@ -595,7 +591,7 @@ export const obterPerfil = query({
|
||||
});
|
||||
|
||||
/**
|
||||
* Listar todos usuários para o chat (com avatar, foto e status)
|
||||
* Listar todos usuários para o chat (com foto e status)
|
||||
*/
|
||||
export const listarParaChat = query({
|
||||
args: {},
|
||||
@@ -605,7 +601,6 @@ export const listarParaChat = query({
|
||||
nome: v.string(),
|
||||
email: v.string(),
|
||||
matricula: v.optional(v.string()),
|
||||
avatar: v.optional(v.string()),
|
||||
fotoPerfil: v.optional(v.id('_storage')),
|
||||
fotoPerfilUrl: v.union(v.string(), v.null()),
|
||||
statusPresenca: v.optional(
|
||||
@@ -656,7 +651,6 @@ export const listarParaChat = query({
|
||||
nome: usuario.nome,
|
||||
email: usuario.email,
|
||||
matricula: matricula || undefined,
|
||||
avatar: usuario.avatar,
|
||||
fotoPerfil: usuario.fotoPerfil,
|
||||
fotoPerfilUrl,
|
||||
statusPresenca: usuario.statusPresenca || 'offline',
|
||||
|
||||
Reference in New Issue
Block a user