687 lines
23 KiB
TypeScript
687 lines
23 KiB
TypeScript
import { defineSchema, defineTable } from "convex/server";
|
|
import { Infer, v } from "convex/values";
|
|
|
|
export const simboloTipo = v.union(
|
|
v.literal("cargo_comissionado"),
|
|
v.literal("funcao_gratificada")
|
|
);
|
|
export type SimboloTipo = Infer<typeof simboloTipo>;
|
|
|
|
export default defineSchema({
|
|
todos: defineTable({
|
|
text: v.string(),
|
|
completed: v.boolean(),
|
|
}),
|
|
funcionarios: defineTable({
|
|
// Campos obrigatórios existentes
|
|
nome: v.string(),
|
|
nascimento: v.string(),
|
|
rg: v.string(),
|
|
cpf: v.string(),
|
|
endereco: v.string(),
|
|
cep: v.string(),
|
|
cidade: v.string(),
|
|
uf: v.string(),
|
|
telefone: v.string(),
|
|
email: v.string(),
|
|
matricula: v.optional(v.string()),
|
|
admissaoData: v.optional(v.string()),
|
|
desligamentoData: v.optional(v.string()),
|
|
simboloId: v.id("simbolos"),
|
|
simboloTipo: simboloTipo,
|
|
gestorId: v.optional(v.id("usuarios")),
|
|
statusFerias: v.optional(v.union(
|
|
v.literal("ativo"),
|
|
v.literal("em_ferias")
|
|
)),
|
|
|
|
// Regime de trabalho (para cálculo correto de férias)
|
|
regimeTrabalho: v.optional(v.union(
|
|
v.literal("clt"), // CLT - Consolidação das Leis do Trabalho
|
|
v.literal("estatutario_pe"), // Servidor Público Estadual de Pernambuco
|
|
v.literal("estatutario_federal"), // Servidor Público Federal
|
|
v.literal("estatutario_municipal") // Servidor Público Municipal
|
|
)),
|
|
|
|
// Dados Pessoais Adicionais (opcionais)
|
|
nomePai: v.optional(v.string()),
|
|
nomeMae: v.optional(v.string()),
|
|
naturalidade: v.optional(v.string()),
|
|
naturalidadeUF: v.optional(v.string()),
|
|
sexo: v.optional(
|
|
v.union(v.literal("masculino"), v.literal("feminino"), v.literal("outro"))
|
|
),
|
|
estadoCivil: v.optional(
|
|
v.union(
|
|
v.literal("solteiro"),
|
|
v.literal("casado"),
|
|
v.literal("divorciado"),
|
|
v.literal("viuvo"),
|
|
v.literal("uniao_estavel")
|
|
)
|
|
),
|
|
nacionalidade: v.optional(v.string()),
|
|
|
|
// Documentos Pessoais
|
|
rgOrgaoExpedidor: v.optional(v.string()),
|
|
rgDataEmissao: v.optional(v.string()),
|
|
carteiraProfissionalNumero: v.optional(v.string()),
|
|
carteiraProfissionalSerie: v.optional(v.string()),
|
|
carteiraProfissionalDataEmissao: v.optional(v.string()),
|
|
reservistaNumero: v.optional(v.string()),
|
|
reservistaSerie: v.optional(v.string()),
|
|
tituloEleitorNumero: v.optional(v.string()),
|
|
tituloEleitorZona: v.optional(v.string()),
|
|
tituloEleitorSecao: v.optional(v.string()),
|
|
pisNumero: v.optional(v.string()),
|
|
|
|
// Formação e Saúde
|
|
grauInstrucao: v.optional(
|
|
v.union(
|
|
v.literal("fundamental"),
|
|
v.literal("medio"),
|
|
v.literal("superior"),
|
|
v.literal("pos_graduacao"),
|
|
v.literal("mestrado"),
|
|
v.literal("doutorado")
|
|
)
|
|
),
|
|
formacao: v.optional(v.string()),
|
|
formacaoRegistro: v.optional(v.string()),
|
|
grupoSanguineo: v.optional(
|
|
v.union(v.literal("A"), v.literal("B"), v.literal("AB"), v.literal("O"))
|
|
),
|
|
fatorRH: v.optional(v.union(v.literal("positivo"), v.literal("negativo"))),
|
|
|
|
// Cargo e Vínculo
|
|
descricaoCargo: v.optional(v.string()),
|
|
nomeacaoPortaria: v.optional(v.string()),
|
|
nomeacaoData: v.optional(v.string()),
|
|
nomeacaoDOE: v.optional(v.string()),
|
|
pertenceOrgaoPublico: v.optional(v.boolean()),
|
|
orgaoOrigem: v.optional(v.string()),
|
|
aposentado: v.optional(
|
|
v.union(v.literal("nao"), v.literal("funape_ipsep"), v.literal("inss"))
|
|
),
|
|
|
|
// Dados Bancários
|
|
contaBradescoNumero: v.optional(v.string()),
|
|
contaBradescoDV: v.optional(v.string()),
|
|
contaBradescoAgencia: v.optional(v.string()),
|
|
|
|
// Documentos Anexos (Storage IDs)
|
|
certidaoAntecedentesPF: v.optional(v.id("_storage")),
|
|
certidaoAntecedentesJFPE: v.optional(v.id("_storage")),
|
|
certidaoAntecedentesSDS: v.optional(v.id("_storage")),
|
|
certidaoAntecedentesTJPE: v.optional(v.id("_storage")),
|
|
certidaoImprobidade: v.optional(v.id("_storage")),
|
|
rgFrente: v.optional(v.id("_storage")),
|
|
rgVerso: v.optional(v.id("_storage")),
|
|
cpfFrente: v.optional(v.id("_storage")),
|
|
cpfVerso: v.optional(v.id("_storage")),
|
|
situacaoCadastralCPF: v.optional(v.id("_storage")),
|
|
tituloEleitorFrente: v.optional(v.id("_storage")),
|
|
tituloEleitorVerso: v.optional(v.id("_storage")),
|
|
comprovanteVotacao: v.optional(v.id("_storage")),
|
|
carteiraProfissionalFrente: v.optional(v.id("_storage")),
|
|
carteiraProfissionalVerso: v.optional(v.id("_storage")),
|
|
comprovantePIS: v.optional(v.id("_storage")),
|
|
certidaoRegistroCivil: v.optional(v.id("_storage")),
|
|
certidaoNascimentoDependentes: v.optional(v.id("_storage")),
|
|
cpfDependentes: v.optional(v.id("_storage")),
|
|
reservistaDoc: v.optional(v.id("_storage")),
|
|
comprovanteEscolaridade: v.optional(v.id("_storage")),
|
|
comprovanteResidencia: v.optional(v.id("_storage")),
|
|
comprovanteContaBradesco: v.optional(v.id("_storage")),
|
|
|
|
// Declarações (Storage IDs)
|
|
declaracaoAcumulacaoCargo: v.optional(v.id("_storage")),
|
|
declaracaoDependentesIR: v.optional(v.id("_storage")),
|
|
declaracaoIdoneidade: v.optional(v.id("_storage")),
|
|
termoNepotismo: v.optional(v.id("_storage")),
|
|
termoOpcaoRemuneracao: v.optional(v.id("_storage")),
|
|
})
|
|
.index("by_matricula", ["matricula"])
|
|
.index("by_nome", ["nome"])
|
|
.index("by_simboloId", ["simboloId"])
|
|
.index("by_simboloTipo", ["simboloTipo"])
|
|
.index("by_cpf", ["cpf"])
|
|
.index("by_rg", ["rg"])
|
|
.index("by_gestor", ["gestorId"]),
|
|
|
|
atestados: defineTable({
|
|
funcionarioId: v.id("funcionarios"),
|
|
dataInicio: v.string(),
|
|
dataFim: v.string(),
|
|
cid: v.string(),
|
|
descricao: v.string(),
|
|
}),
|
|
|
|
solicitacoesFerias: defineTable({
|
|
funcionarioId: v.id("funcionarios"),
|
|
anoReferencia: v.number(),
|
|
status: v.union(
|
|
v.literal("aguardando_aprovacao"),
|
|
v.literal("aprovado"),
|
|
v.literal("reprovado"),
|
|
v.literal("data_ajustada_aprovada")
|
|
),
|
|
periodos: v.array(
|
|
v.object({
|
|
dataInicio: v.string(),
|
|
dataFim: v.string(),
|
|
diasCorridos: v.number(),
|
|
})
|
|
),
|
|
observacao: v.optional(v.string()),
|
|
motivoReprovacao: v.optional(v.string()),
|
|
gestorId: v.optional(v.id("usuarios")),
|
|
dataAprovacao: v.optional(v.number()),
|
|
dataReprovacao: v.optional(v.number()),
|
|
historicoAlteracoes: v.optional(
|
|
v.array(
|
|
v.object({
|
|
data: v.number(),
|
|
usuarioId: v.id("usuarios"),
|
|
acao: v.string(),
|
|
periodosAnteriores: v.optional(v.array(v.object({
|
|
dataInicio: v.string(),
|
|
dataFim: v.string(),
|
|
diasCorridos: v.number(),
|
|
}))),
|
|
})
|
|
)
|
|
),
|
|
})
|
|
.index("by_funcionario", ["funcionarioId"])
|
|
.index("by_status", ["status"])
|
|
.index("by_funcionario_and_status", ["funcionarioId", "status"])
|
|
.index("by_ano", ["anoReferencia"]),
|
|
|
|
notificacoesFerias: defineTable({
|
|
destinatarioId: v.id("usuarios"),
|
|
solicitacaoFeriasId: v.id("solicitacoesFerias"),
|
|
tipo: v.union(
|
|
v.literal("nova_solicitacao"),
|
|
v.literal("aprovado"),
|
|
v.literal("reprovado"),
|
|
v.literal("data_ajustada")
|
|
),
|
|
lida: v.boolean(),
|
|
mensagem: v.string(),
|
|
})
|
|
.index("by_destinatario", ["destinatarioId"])
|
|
.index("by_destinatario_and_lida", ["destinatarioId", "lida"]),
|
|
|
|
// Períodos aquisitivos e saldos de férias
|
|
periodosAquisitivos: defineTable({
|
|
funcionarioId: v.id("funcionarios"),
|
|
anoReferencia: v.number(), // Ano do período aquisitivo (ex: 2024)
|
|
dataInicio: v.string(), // Data de início do período aquisitivo
|
|
dataFim: v.string(), // Data de fim do período aquisitivo
|
|
diasDireito: v.number(), // Dias de férias que tem direito (30 ou proporcional)
|
|
diasUsados: v.number(), // Dias já usados
|
|
diasPendentes: v.number(), // Dias em solicitações aguardando aprovação
|
|
diasDisponiveis: v.number(), // Dias disponíveis = direito - usados - pendentes
|
|
abonoPermitido: v.boolean(), // Se pode vender 1/3 das férias
|
|
diasAbono: v.number(), // Dias vendidos como abono pecuniário
|
|
status: v.union(
|
|
v.literal("ativo"), // Período vigente
|
|
v.literal("vencido"), // Período vencido (não tirou férias)
|
|
v.literal("concluido") // Período totalmente utilizado
|
|
),
|
|
})
|
|
.index("by_funcionario", ["funcionarioId"])
|
|
.index("by_funcionario_and_ano", ["funcionarioId", "anoReferencia"])
|
|
.index("by_funcionario_and_status", ["funcionarioId", "status"]),
|
|
|
|
times: defineTable({
|
|
nome: v.string(),
|
|
descricao: v.optional(v.string()),
|
|
gestorId: v.id("usuarios"),
|
|
ativo: v.boolean(),
|
|
cor: v.optional(v.string()), // Cor para identificação visual
|
|
}).index("by_gestor", ["gestorId"]),
|
|
|
|
timesMembros: defineTable({
|
|
timeId: v.id("times"),
|
|
funcionarioId: v.id("funcionarios"),
|
|
dataEntrada: v.number(),
|
|
dataSaida: v.optional(v.number()),
|
|
ativo: v.boolean(),
|
|
})
|
|
.index("by_time", ["timeId"])
|
|
.index("by_funcionario", ["funcionarioId"])
|
|
.index("by_time_and_ativo", ["timeId", "ativo"]),
|
|
|
|
cursos: defineTable({
|
|
funcionarioId: v.id("funcionarios"),
|
|
descricao: v.string(),
|
|
data: v.string(),
|
|
certificadoId: v.optional(v.id("_storage")),
|
|
}).index("by_funcionario", ["funcionarioId"]),
|
|
|
|
simbolos: defineTable({
|
|
nome: v.string(),
|
|
tipo: simboloTipo,
|
|
descricao: v.string(),
|
|
vencValor: v.string(),
|
|
repValor: v.string(),
|
|
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({
|
|
matricula: v.string(),
|
|
senhaHash: v.string(), // Senha criptografada com bcrypt
|
|
nome: v.string(),
|
|
email: v.string(),
|
|
funcionarioId: v.optional(v.id("funcionarios")),
|
|
roleId: v.id("roles"),
|
|
ativo: v.boolean(),
|
|
primeiroAcesso: v.boolean(),
|
|
ultimoAcesso: v.optional(v.number()),
|
|
criadoEm: v.number(),
|
|
atualizadoEm: v.number(),
|
|
|
|
// Controle de Bloqueio e Segurança
|
|
bloqueado: v.optional(v.boolean()),
|
|
motivoBloqueio: v.optional(v.string()),
|
|
dataBloqueio: v.optional(v.number()),
|
|
tentativasLogin: v.optional(v.number()), // contador de tentativas falhas
|
|
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
|
|
statusPresenca: v.optional(
|
|
v.union(
|
|
v.literal("online"),
|
|
v.literal("offline"),
|
|
v.literal("ausente"),
|
|
v.literal("externo"),
|
|
v.literal("em_reuniao")
|
|
)
|
|
),
|
|
ultimaAtividade: v.optional(v.number()), // timestamp
|
|
notificacoesAtivadas: v.optional(v.boolean()),
|
|
somNotificacao: v.optional(v.boolean()),
|
|
})
|
|
.index("by_matricula", ["matricula"])
|
|
.index("by_email", ["email"])
|
|
.index("by_role", ["roleId"])
|
|
.index("by_ativo", ["ativo"])
|
|
.index("by_status_presenca", ["statusPresenca"])
|
|
.index("by_bloqueado", ["bloqueado"])
|
|
.index("by_funcionarioId", ["funcionarioId"]),
|
|
|
|
roles: defineTable({
|
|
nome: v.string(), // "admin", "ti_master", "ti_usuario", "usuario_avancado", "usuario"
|
|
descricao: v.string(),
|
|
nivel: v.number(), // 0 = admin, 1 = ti_master, 2 = ti_usuario, 3+ = customizado
|
|
setor: v.optional(v.string()), // "ti", "rh", "financeiro", etc.
|
|
customizado: v.optional(v.boolean()), // se é um perfil customizado criado por TI_MASTER
|
|
criadoPor: v.optional(v.id("usuarios")), // usuário TI_MASTER que criou este perfil
|
|
editavel: v.optional(v.boolean()), // se pode ser editado (false para roles fixas)
|
|
})
|
|
.index("by_nome", ["nome"])
|
|
.index("by_nivel", ["nivel"])
|
|
.index("by_setor", ["setor"])
|
|
.index("by_customizado", ["customizado"]),
|
|
|
|
permissoes: defineTable({
|
|
nome: v.string(), // "funcionarios.criar", "simbolos.editar", etc.
|
|
descricao: v.string(),
|
|
recurso: v.string(), // "funcionarios", "simbolos", "usuarios", etc.
|
|
acao: v.string(), // "criar", "ler", "editar", "excluir"
|
|
})
|
|
.index("by_recurso", ["recurso"])
|
|
.index("by_recurso_e_acao", ["recurso", "acao"])
|
|
.index("by_nome", ["nome"]),
|
|
|
|
rolePermissoes: defineTable({
|
|
roleId: v.id("roles"),
|
|
permissaoId: v.id("permissoes"),
|
|
})
|
|
.index("by_role", ["roleId"])
|
|
.index("by_permissao", ["permissaoId"]),
|
|
|
|
// Permissões de Menu (granulares por role)
|
|
menuPermissoes: defineTable({
|
|
roleId: v.id("roles"),
|
|
menuPath: v.string(), // "/recursos-humanos", "/financeiro", etc.
|
|
podeAcessar: v.boolean(),
|
|
podeConsultar: v.boolean(), // Pode apenas visualizar
|
|
podeGravar: v.boolean(), // Pode criar/editar/excluir
|
|
})
|
|
.index("by_role", ["roleId"])
|
|
.index("by_menu", ["menuPath"])
|
|
.index("by_role_and_menu", ["roleId", "menuPath"]),
|
|
|
|
// Permissões de Menu Personalizadas (por matrícula)
|
|
menuPermissoesPersonalizadas: defineTable({
|
|
usuarioId: v.id("usuarios"),
|
|
matricula: v.string(), // Para facilitar busca
|
|
menuPath: v.string(),
|
|
podeAcessar: v.boolean(),
|
|
podeConsultar: v.boolean(),
|
|
podeGravar: v.boolean(),
|
|
})
|
|
.index("by_usuario", ["usuarioId"])
|
|
.index("by_matricula", ["matricula"])
|
|
.index("by_usuario_and_menu", ["usuarioId", "menuPath"])
|
|
.index("by_matricula_and_menu", ["matricula", "menuPath"]),
|
|
|
|
sessoes: defineTable({
|
|
usuarioId: v.id("usuarios"),
|
|
token: v.string(),
|
|
ipAddress: v.optional(v.string()),
|
|
userAgent: v.optional(v.string()),
|
|
criadoEm: v.number(),
|
|
expiraEm: v.number(),
|
|
ativo: v.boolean(),
|
|
})
|
|
.index("by_usuario", ["usuarioId"])
|
|
.index("by_token", ["token"])
|
|
.index("by_ativo", ["ativo"])
|
|
.index("by_expiracao", ["expiraEm"]),
|
|
|
|
logsAcesso: defineTable({
|
|
usuarioId: v.id("usuarios"),
|
|
tipo: v.union(
|
|
v.literal("login"),
|
|
v.literal("logout"),
|
|
v.literal("acesso_negado"),
|
|
v.literal("senha_alterada"),
|
|
v.literal("sessao_expirada")
|
|
),
|
|
ipAddress: v.optional(v.string()),
|
|
userAgent: v.optional(v.string()),
|
|
detalhes: v.optional(v.string()),
|
|
timestamp: v.number(),
|
|
})
|
|
.index("by_usuario", ["usuarioId"])
|
|
.index("by_tipo", ["tipo"])
|
|
.index("by_timestamp", ["timestamp"]),
|
|
|
|
// Logs de Login Detalhados
|
|
logsLogin: defineTable({
|
|
usuarioId: v.optional(v.id("usuarios")), // pode ser null se falha antes de identificar usuário
|
|
matriculaOuEmail: v.string(), // tentativa de login
|
|
sucesso: v.boolean(),
|
|
motivoFalha: v.optional(v.string()), // "senha_incorreta", "usuario_bloqueado", "usuario_inexistente"
|
|
ipAddress: v.optional(v.string()),
|
|
userAgent: v.optional(v.string()),
|
|
device: v.optional(v.string()),
|
|
browser: v.optional(v.string()),
|
|
sistema: v.optional(v.string()),
|
|
timestamp: v.number(),
|
|
})
|
|
.index("by_usuario", ["usuarioId"])
|
|
.index("by_sucesso", ["sucesso"])
|
|
.index("by_timestamp", ["timestamp"])
|
|
.index("by_ip", ["ipAddress"]),
|
|
|
|
// Logs de Atividades
|
|
logsAtividades: defineTable({
|
|
usuarioId: v.id("usuarios"),
|
|
acao: v.string(), // "criar", "editar", "excluir", "bloquear", "desbloquear", etc.
|
|
recurso: v.string(), // "funcionarios", "simbolos", "usuarios", "perfis", etc.
|
|
recursoId: v.optional(v.string()), // ID do recurso afetado
|
|
detalhes: v.optional(v.string()), // JSON com detalhes da ação
|
|
timestamp: v.number(),
|
|
})
|
|
.index("by_usuario", ["usuarioId"])
|
|
.index("by_acao", ["acao"])
|
|
.index("by_recurso", ["recurso"])
|
|
.index("by_timestamp", ["timestamp"])
|
|
.index("by_recurso_id", ["recurso", "recursoId"]),
|
|
|
|
// Histórico de Bloqueios
|
|
bloqueiosUsuarios: defineTable({
|
|
usuarioId: v.id("usuarios"),
|
|
motivo: v.string(),
|
|
bloqueadoPor: v.id("usuarios"), // ID do TI_MASTER que bloqueou
|
|
dataInicio: v.number(),
|
|
dataFim: v.optional(v.number()), // quando foi desbloqueado
|
|
desbloqueadoPor: v.optional(v.id("usuarios")),
|
|
ativo: v.boolean(), // se é o bloqueio atual ativo
|
|
})
|
|
.index("by_usuario", ["usuarioId"])
|
|
.index("by_bloqueado_por", ["bloqueadoPor"])
|
|
.index("by_ativo", ["ativo"])
|
|
.index("by_data_inicio", ["dataInicio"]),
|
|
|
|
// Perfis Customizados
|
|
perfisCustomizados: defineTable({
|
|
nome: v.string(),
|
|
descricao: v.string(),
|
|
nivel: v.number(), // >= 3
|
|
roleId: v.id("roles"), // role correspondente criada
|
|
criadoPor: v.id("usuarios"), // TI_MASTER que criou
|
|
criadoEm: v.number(),
|
|
atualizadoEm: v.number(),
|
|
})
|
|
.index("by_nome", ["nome"])
|
|
.index("by_nivel", ["nivel"])
|
|
.index("by_criado_por", ["criadoPor"])
|
|
.index("by_role", ["roleId"]),
|
|
|
|
// Templates de Mensagens
|
|
templatesMensagens: defineTable({
|
|
codigo: v.string(), // "USUARIO_BLOQUEADO", "SENHA_RESETADA", etc.
|
|
nome: v.string(),
|
|
tipo: v.union(
|
|
v.literal("sistema"), // predefinido, não editável
|
|
v.literal("customizado") // criado por TI_MASTER
|
|
),
|
|
titulo: v.string(),
|
|
corpo: v.string(), // pode ter variáveis {{variavel}}
|
|
variaveis: v.optional(v.array(v.string())), // ["motivo", "senha", etc.]
|
|
criadoPor: v.optional(v.id("usuarios")),
|
|
criadoEm: v.number(),
|
|
})
|
|
.index("by_codigo", ["codigo"])
|
|
.index("by_tipo", ["tipo"])
|
|
.index("by_criado_por", ["criadoPor"]),
|
|
|
|
// Configuração de Email/SMTP
|
|
configuracaoEmail: defineTable({
|
|
servidor: v.string(), // smtp.gmail.com
|
|
porta: v.number(), // 587, 465, etc.
|
|
usuario: v.string(),
|
|
senhaHash: v.string(), // senha criptografada
|
|
emailRemetente: v.string(),
|
|
nomeRemetente: v.string(),
|
|
usarSSL: v.boolean(),
|
|
usarTLS: v.boolean(),
|
|
ativo: v.boolean(),
|
|
testadoEm: v.optional(v.number()),
|
|
configuradoPor: v.id("usuarios"),
|
|
atualizadoEm: v.number(),
|
|
}).index("by_ativo", ["ativo"]),
|
|
|
|
// Fila de Emails
|
|
notificacoesEmail: defineTable({
|
|
destinatario: v.string(), // email
|
|
destinatarioId: v.optional(v.id("usuarios")),
|
|
assunto: v.string(),
|
|
corpo: v.string(), // HTML ou texto
|
|
templateId: v.optional(v.id("templatesMensagens")),
|
|
status: v.union(
|
|
v.literal("pendente"),
|
|
v.literal("enviando"),
|
|
v.literal("enviado"),
|
|
v.literal("falha")
|
|
),
|
|
tentativas: v.number(),
|
|
ultimaTentativa: v.optional(v.number()),
|
|
erroDetalhes: v.optional(v.string()),
|
|
enviadoPor: v.id("usuarios"),
|
|
criadoEm: v.number(),
|
|
enviadoEm: v.optional(v.number()),
|
|
})
|
|
.index("by_status", ["status"])
|
|
.index("by_destinatario", ["destinatarioId"])
|
|
.index("by_enviado_por", ["enviadoPor"])
|
|
.index("by_criado_em", ["criadoEm"]),
|
|
|
|
configuracaoAcesso: defineTable({
|
|
chave: v.string(), // "sessao_duracao", "max_tentativas_login", etc.
|
|
valor: v.string(),
|
|
descricao: v.string(),
|
|
}).index("by_chave", ["chave"]),
|
|
|
|
// Sistema de Chat
|
|
conversas: defineTable({
|
|
tipo: v.union(v.literal("individual"), v.literal("grupo")),
|
|
nome: v.optional(v.string()), // nome do grupo
|
|
avatar: v.optional(v.string()), // avatar do grupo
|
|
participantes: v.array(v.id("usuarios")), // IDs dos participantes
|
|
ultimaMensagem: v.optional(v.string()),
|
|
ultimaMensagemTimestamp: v.optional(v.number()),
|
|
criadoPor: v.id("usuarios"),
|
|
criadoEm: v.number(),
|
|
})
|
|
.index("by_criado_por", ["criadoPor"])
|
|
.index("by_tipo", ["tipo"])
|
|
.index("by_ultima_mensagem", ["ultimaMensagemTimestamp"]),
|
|
|
|
mensagens: defineTable({
|
|
conversaId: v.id("conversas"),
|
|
remetenteId: v.id("usuarios"),
|
|
tipo: v.union(
|
|
v.literal("texto"),
|
|
v.literal("arquivo"),
|
|
v.literal("imagem")
|
|
),
|
|
conteudo: v.string(), // texto ou nome do arquivo
|
|
arquivoId: v.optional(v.id("_storage")),
|
|
arquivoNome: v.optional(v.string()),
|
|
arquivoTamanho: v.optional(v.number()),
|
|
arquivoTipo: v.optional(v.string()),
|
|
reagiuPor: v.optional(
|
|
v.array(
|
|
v.object({
|
|
usuarioId: v.id("usuarios"),
|
|
emoji: v.string(),
|
|
})
|
|
)
|
|
),
|
|
mencoes: v.optional(v.array(v.id("usuarios"))),
|
|
agendadaPara: v.optional(v.number()), // timestamp
|
|
enviadaEm: v.number(),
|
|
editadaEm: v.optional(v.number()),
|
|
deletada: v.optional(v.boolean()),
|
|
})
|
|
.index("by_conversa", ["conversaId", "enviadaEm"])
|
|
.index("by_remetente", ["remetenteId"])
|
|
.index("by_agendamento", ["agendadaPara"]),
|
|
|
|
leituras: defineTable({
|
|
conversaId: v.id("conversas"),
|
|
usuarioId: v.id("usuarios"),
|
|
ultimaMensagemLida: v.id("mensagens"),
|
|
lidaEm: v.number(),
|
|
})
|
|
.index("by_conversa_usuario", ["conversaId", "usuarioId"])
|
|
.index("by_usuario", ["usuarioId"]),
|
|
|
|
notificacoes: defineTable({
|
|
usuarioId: v.id("usuarios"),
|
|
tipo: v.union(
|
|
v.literal("nova_mensagem"),
|
|
v.literal("mencao"),
|
|
v.literal("grupo_criado"),
|
|
v.literal("adicionado_grupo")
|
|
),
|
|
conversaId: v.optional(v.id("conversas")),
|
|
mensagemId: v.optional(v.id("mensagens")),
|
|
remetenteId: v.optional(v.id("usuarios")),
|
|
titulo: v.string(),
|
|
descricao: v.string(),
|
|
lida: v.boolean(),
|
|
criadaEm: v.number(),
|
|
})
|
|
.index("by_usuario", ["usuarioId", "lida", "criadaEm"])
|
|
.index("by_usuario_lida", ["usuarioId", "lida"]),
|
|
|
|
digitando: defineTable({
|
|
conversaId: v.id("conversas"),
|
|
usuarioId: v.id("usuarios"),
|
|
iniciouEm: v.number(),
|
|
})
|
|
.index("by_conversa", ["conversaId", "iniciouEm"])
|
|
.index("by_usuario", ["usuarioId"]),
|
|
|
|
// Tabelas de Monitoramento do Sistema
|
|
systemMetrics: defineTable({
|
|
timestamp: v.number(),
|
|
// Métricas de Sistema
|
|
cpuUsage: v.optional(v.number()),
|
|
memoryUsage: v.optional(v.number()),
|
|
networkLatency: v.optional(v.number()),
|
|
storageUsed: v.optional(v.number()),
|
|
// Métricas de Aplicação
|
|
usuariosOnline: v.optional(v.number()),
|
|
mensagensPorMinuto: v.optional(v.number()),
|
|
tempoRespostaMedio: v.optional(v.number()),
|
|
errosCount: v.optional(v.number()),
|
|
})
|
|
.index("by_timestamp", ["timestamp"]),
|
|
|
|
alertConfigurations: defineTable({
|
|
metricName: v.string(),
|
|
threshold: v.number(),
|
|
operator: v.union(
|
|
v.literal(">"),
|
|
v.literal("<"),
|
|
v.literal(">="),
|
|
v.literal("<="),
|
|
v.literal("==")
|
|
),
|
|
enabled: v.boolean(),
|
|
notifyByEmail: v.boolean(),
|
|
notifyByChat: v.boolean(),
|
|
createdBy: v.id("usuarios"),
|
|
lastModified: v.number(),
|
|
})
|
|
.index("by_enabled", ["enabled"]),
|
|
|
|
alertHistory: defineTable({
|
|
configId: v.id("alertConfigurations"),
|
|
metricName: v.string(),
|
|
metricValue: v.number(),
|
|
threshold: v.number(),
|
|
timestamp: v.number(),
|
|
status: v.union(v.literal("triggered"), v.literal("resolved")),
|
|
notificationsSent: v.object({
|
|
email: v.boolean(),
|
|
chat: v.boolean(),
|
|
}),
|
|
})
|
|
.index("by_timestamp", ["timestamp"])
|
|
.index("by_status", ["status"])
|
|
.index("by_config", ["configId", "timestamp"]),
|
|
});
|