import { v } from 'convex/values'; import { mutation, query } from './_generated/server'; import type { Id } from './_generated/dataModel'; import { getCurrentUserFunction } from './auth'; import { internal } from './_generated/api'; export const list = query({ args: { periodoInicio: v.optional(v.string()), periodoFim: v.optional(v.string()), numero: v.optional(v.string()), numeroSei: v.optional(v.string()) }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: 'atas', acao: 'listar' }); const numero = args.numero?.trim().toLowerCase(); const numeroSei = args.numeroSei?.trim().toLowerCase(); const periodoInicio = args.periodoInicio || undefined; const periodoFim = args.periodoFim || undefined; const atas = await ctx.db.query('atas').collect(); return atas.filter((ata) => { const numeroOk = !numero || (ata.numero || '').toLowerCase().includes(numero); const seiOk = !numeroSei || (ata.numeroSei || '').toLowerCase().includes(numeroSei); // Filtro por intervalo (range): retorna atas cuja vigência intersecta o período informado. // Considera datas como strings "YYYY-MM-DD" (lexicograficamente comparáveis). const ataInicio = ata.dataInicio ?? '0000-01-01'; const ataFim = ata.dataFim ?? '9999-12-31'; const periodoOk = (!periodoInicio && !periodoFim) || (periodoInicio && periodoFim && ataInicio <= periodoFim && ataFim >= periodoInicio) || (periodoInicio && !periodoFim && ataFim >= periodoInicio) || (!periodoInicio && periodoFim && ataInicio <= periodoFim); return numeroOk && seiOk && periodoOk; }); } }); export const get = query({ args: { id: v.id('atas') }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: 'atas', acao: 'ver' }); return await ctx.db.get(args.id); } }); export const getObjetos = query({ args: { id: v.id('atas') }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: 'atas', acao: 'ver' }); const links = await ctx.db .query('atasObjetos') .withIndex('by_ataId', (q) => q.eq('ataId', args.id)) .collect(); const objetos = await Promise.all(links.map((link) => ctx.db.get(link.objetoId))); return objetos.filter((obj) => obj !== null); } }); export const listByObjetoIds = query({ args: { objetoIds: v.array(v.id('objetos')) }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: 'atas', acao: 'listar' }); if (args.objetoIds.length === 0) return []; // Buscar todos os vínculos ata-objeto para os objetos informados const links = []; for (const objetoId of args.objetoIds) { const partial = await ctx.db .query('atasObjetos') .withIndex('by_objetoId', (q) => q.eq('objetoId', objetoId as Id<'objetos'>)) .collect(); links.push(...partial); } const ataIds = Array.from(new Set(links.map((l) => l.ataId as Id<'atas'>))); if (ataIds.length === 0) return []; const atas = await Promise.all(ataIds.map((id) => ctx.db.get(id))); return atas.filter((a): a is NonNullable => a !== null); } }); export const create = mutation({ args: { numero: v.string(), dataInicio: v.optional(v.string()), dataFim: v.optional(v.string()), empresaId: v.id('empresas'), numeroSei: v.string(), objetosIds: v.array(v.id('objetos')) }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: 'atas', acao: 'criar' }); const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); const ataId = await ctx.db.insert('atas', { numero: args.numero, numeroSei: args.numeroSei, empresaId: args.empresaId, dataInicio: args.dataInicio, dataFim: args.dataFim, criadoPor: user._id, criadoEm: Date.now(), atualizadoEm: Date.now() }); // Vincular objetos for (const objetoId of args.objetosIds) { await ctx.db.insert('atasObjetos', { ataId, objetoId }); } return ataId; } }); export const update = mutation({ args: { id: v.id('atas'), numero: v.string(), numeroSei: v.string(), empresaId: v.id('empresas'), dataInicio: v.optional(v.string()), dataFim: v.optional(v.string()), objetosIds: v.array(v.id('objetos')) }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: 'atas', acao: 'editar' }); const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); await ctx.db.patch(args.id, { numero: args.numero, numeroSei: args.numeroSei, empresaId: args.empresaId, dataInicio: args.dataInicio, dataFim: args.dataFim, atualizadoEm: Date.now() }); // Atualizar objetos vinculados // Primeiro remove todos os vínculos existentes const existingLinks = await ctx.db .query('atasObjetos') .withIndex('by_ataId', (q) => q.eq('ataId', args.id)) .collect(); for (const link of existingLinks) { await ctx.db.delete(link._id); } // Adiciona os novos vínculos for (const objetoId of args.objetosIds) { await ctx.db.insert('atasObjetos', { ataId: args.id, objetoId }); } } }); export const remove = mutation({ args: { id: v.id('atas') }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: 'atas', acao: 'excluir' }); const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); // Remover vínculos com objetos const links = await ctx.db .query('atasObjetos') .withIndex('by_ataId', (q) => q.eq('ataId', args.id)) .collect(); for (const link of links) { await ctx.db.delete(link._id); } // Remover documentos vinculados const docs = await ctx.db .query('atasDocumentos') .withIndex('by_ataId', (q) => q.eq('ataId', args.id)) .collect(); for (const doc of docs) { await ctx.storage.delete(doc.storageId); await ctx.db.delete(doc._id); } await ctx.db.delete(args.id); } }); export const generateUploadUrl = mutation({ args: {}, handler: async (ctx) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: 'atas', acao: 'editar' }); return await ctx.storage.generateUploadUrl(); } }); export const saveDocumento = mutation({ args: { ataId: v.id('atas'), nome: v.string(), storageId: v.id('_storage'), tipo: v.string(), tamanho: v.number() }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: 'atas', acao: 'editar' }); const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); return await ctx.db.insert('atasDocumentos', { ataId: args.ataId, nome: args.nome, storageId: args.storageId, tipo: args.tipo, tamanho: args.tamanho, criadoPor: user._id, criadoEm: Date.now() }); } }); export const removeDocumento = mutation({ args: { id: v.id('atasDocumentos') }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: 'atas', acao: 'editar' }); const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); const doc = await ctx.db.get(args.id); if (!doc) throw new Error('Documento não encontrado'); await ctx.storage.delete(doc.storageId); await ctx.db.delete(args.id); } }); export const getDocumentos = query({ args: { ataId: v.id('atas') }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: 'atas', acao: 'ver' }); const docs = await ctx.db .query('atasDocumentos') .withIndex('by_ataId', (q) => q.eq('ataId', args.ataId)) .collect(); return await Promise.all( docs.map(async (doc) => ({ ...doc, url: await ctx.storage.getUrl(doc.storageId) })) ); } });