feat: Introduce structured table definitions in convex/tables for various entities and remove the todos example table.

This commit is contained in:
2025-12-02 09:55:07 -03:00
parent 1c0bd219b2
commit 05e7f1181d
30 changed files with 2700 additions and 2535 deletions

View File

@@ -4,7 +4,7 @@ import { v } from 'convex/values';
import { getCurrentUserFunction } from './auth';
import type { Id, Doc } from './_generated/dataModel';
import type { MutationCtx, QueryCtx } from './_generated/server';
import { flowTemplateStatus, flowInstanceStatus, flowInstanceStepStatus } from './schema';
import { flowInstanceStatus, flowInstanceStepStatus, flowTemplateStatus } from './tables/flows';
// ============================================
// HELPER FUNCTIONS
@@ -852,7 +852,7 @@ export const getInstanceWithSteps = query({
// Verificar permissão de visualização
const temPermissaoVerTodas = await verificarPermissaoVerTodasFluxos(ctx);
if (!temPermissaoVerTodas) {
// Verificar se usuário pertence a algum setor do fluxo ou é o manager
const pertenceAoSetor = await usuarioPertenceAAlgumSetorDoFluxo(
@@ -860,7 +860,7 @@ export const getInstanceWithSteps = query({
usuario._id,
instance._id
);
if (!pertenceAoSetor && instance.managerId !== usuario._id) {
return null; // Usuário não tem acesso
}
@@ -1066,7 +1066,8 @@ export const instantiateFlow = mutation({
for (let i = 0; i < templateSteps.length; i++) {
const step = templateSteps[i];
const dueDate = now + cumulativeDays * 24 * 60 * 60 * 1000 + step.expectedDuration * 24 * 60 * 60 * 1000;
const dueDate =
now + cumulativeDays * 24 * 60 * 60 * 1000 + step.expectedDuration * 24 * 60 * 60 * 1000;
cumulativeDays += step.expectedDuration;
const instanceStepId = await ctx.db.insert('flowInstanceSteps', {
@@ -1202,7 +1203,12 @@ export const completeStep = mutation({
if (nextSetor && nextFlowStep) {
const tituloProximoSetor = 'Nova Etapa de Fluxo Disponível';
const descricaoProximoSetor = `A etapa "${nextFlowStep.name}" do fluxo "${template?.name ?? 'Fluxo'}" está pronta para ser iniciada.`;
await criarNotificacaoParaSetor(ctx, nextStepData.setorId, tituloProximoSetor, descricaoProximoSetor);
await criarNotificacaoParaSetor(
ctx,
nextStepData.setorId,
tituloProximoSetor,
descricaoProximoSetor
);
}
}
} else {
@@ -1303,7 +1309,9 @@ export const alterarGestorFluxo = mutation({
const eCriador = template?.createdBy === usuario._id;
if (!eGestor && !temPermissao && !eCriador) {
throw new Error('Somente o gestor atual, criador do template ou usuário com permissão pode alterar o gestor');
throw new Error(
'Somente o gestor atual, criador do template ou usuário com permissão pode alterar o gestor'
);
}
// Verificar se novo gestor existe
@@ -1371,7 +1379,7 @@ export const reassignStep = mutation({
if (!eCriador) {
// Se não for criador, verificar regra normal
const etapaAnterior = await obterEtapaAnterior(ctx, args.instanceStepId);
if (etapaAnterior) {
// Se há etapa anterior, verificar se o usuário atual é a pessoa atribuída
if (etapaAnterior.assignedToId) {
@@ -1386,7 +1394,9 @@ export const reassignStep = mutation({
if (instance.managerId !== usuario._id) {
const temPermissao = await verificarPermissaoVerTodasFluxos(ctx);
if (!temPermissao) {
throw new Error('Somente o gerente do fluxo ou pessoa da etapa anterior pode atribuir esta etapa');
throw new Error(
'Somente o gerente do fluxo ou pessoa da etapa anterior pode atribuir esta etapa'
);
}
}
}
@@ -1408,9 +1418,7 @@ export const reassignStep = mutation({
}
// Verificar se o usuário atribuído corresponde a um funcionário do setor
const funcionarioDoUsuario = funcionariosDoSetor.find(
(f) => f.email === assignee.email
);
const funcionarioDoUsuario = funcionariosDoSetor.find((f) => f.email === assignee.email);
if (!funcionarioDoUsuario) {
throw new Error('O funcionário atribuído não pertence ao setor deste passo');
@@ -1441,7 +1449,7 @@ export const updateStepNotes = mutation({
throw new Error('Passo não encontrado');
}
await ctx.db.patch(args.instanceStepId, {
await ctx.db.patch(args.instanceStepId, {
notes: args.notes,
notesUpdatedBy: usuario._id,
notesUpdatedAt: Date.now()
@@ -1526,7 +1534,9 @@ export const listarSubEtapas = query({
} else if (args.flowInstanceStepId) {
subEtapas = await ctx.db
.query('flowSubSteps')
.withIndex('by_flowInstanceStepId', (q) => q.eq('flowInstanceStepId', args.flowInstanceStepId))
.withIndex('by_flowInstanceStepId', (q) =>
q.eq('flowInstanceStepId', args.flowInstanceStepId)
)
.collect();
} else {
return [];
@@ -1607,7 +1617,9 @@ export const criarSubEtapa = mutation({
} else if (args.flowInstanceStepId) {
const existingSubEtapas = await ctx.db
.query('flowSubSteps')
.withIndex('by_flowInstanceStepId', (q) => q.eq('flowInstanceStepId', args.flowInstanceStepId))
.withIndex('by_flowInstanceStepId', (q) =>
q.eq('flowInstanceStepId', args.flowInstanceStepId)
)
.collect();
if (existingSubEtapas.length > 0) {
maxPosition = Math.max(...existingSubEtapas.map((s) => s.position));
@@ -1766,7 +1778,9 @@ export const listarNotas = query({
} else if (args.flowInstanceStepId) {
notas = await ctx.db
.query('flowStepNotes')
.withIndex('by_flowInstanceStepId', (q) => q.eq('flowInstanceStepId', args.flowInstanceStepId))
.withIndex('by_flowInstanceStepId', (q) =>
q.eq('flowInstanceStepId', args.flowInstanceStepId)
)
.collect();
} else if (args.flowSubStepId) {
notas = await ctx.db
@@ -1784,17 +1798,15 @@ export const listarNotas = query({
const notasComDetalhes = await Promise.all(
notas.map(async (nota) => {
const criador = await ctx.db.get(nota.criadoPor);
// Obter informações dos arquivos
const arquivosComNome = await Promise.all(
nota.arquivos.map(async (storageId) => {
// Buscar documento que referencia este storageId
// Como não temos uma tabela direta, vamos buscar nos flowInstanceDocuments
const documentos = await ctx.db
.query('flowInstanceDocuments')
.collect();
const documentos = await ctx.db.query('flowInstanceDocuments').collect();
const documento = documentos.find((d) => d.storageId === storageId);
return {
storageId,
name: documento?.name ?? 'Arquivo'
@@ -2003,7 +2015,9 @@ export const listDocumentsByStep = query({
handler: async (ctx, args) => {
const documents = await ctx.db
.query('flowInstanceDocuments')
.withIndex('by_flowInstanceStepId', (q) => q.eq('flowInstanceStepId', args.flowInstanceStepId))
.withIndex('by_flowInstanceStepId', (q) =>
q.eq('flowInstanceStepId', args.flowInstanceStepId)
)
.collect();
const result: Array<{
@@ -2158,4 +2172,3 @@ export const getUsuariosBySetorForAssignment = query({
return usuarios;
}
});