import { defineSchema, defineTable } from "convex/server"; import { Infer, v } from "convex/values"; import { tables } from "./betterAuth/schema"; import { cidrv4 } from "better-auth"; export const simboloTipo = v.union( v.literal("cargo_comissionado"), v.literal("funcao_gratificada") ); export type SimboloTipo = Infer; export default defineSchema({ ...tables, 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.string(), admissaoData: v.optional(v.string()), desligamentoData: v.optional(v.string()), simboloId: v.id("simbolos"), simboloTipo: simboloTipo, // 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"]), atestados: defineTable({ funcionarioId: v.id("funcionarios"), dataInicio: v.string(), dataFim: v.string(), cid: v.string(), descricao: v.string(), }), ferias: defineTable({ funcionarioId: v.id("funcionarios"), dataInicio: v.string(), dataFim: v.string(), }), 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(), // 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"]), roles: defineTable({ nome: v.string(), // "admin", "ti", "usuario_avancado", "usuario" descricao: v.string(), nivel: v.number(), // 0 = admin, 1 = ti, 2 = usuario_avancado, 3 = usuario setor: v.optional(v.string()), // "ti", "rh", "financeiro", etc. }) .index("by_nome", ["nome"]) .index("by_nivel", ["nivel"]) .index("by_setor", ["setor"]), 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_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"]), 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"]), });