172 lines
5.1 KiB
TypeScript
172 lines
5.1 KiB
TypeScript
import { internalMutation } from "./_generated/server";
|
|
import { v } from "convex/values";
|
|
|
|
/**
|
|
* Migração: Converte estrutura antiga de gestores individuais para times
|
|
*
|
|
* Esta função cria automaticamente times baseados nos gestores existentes
|
|
* e adiciona os funcionários subordinados aos respectivos times.
|
|
*
|
|
* Execute uma vez via dashboard do Convex:
|
|
* Settings > Functions > Internal > migrarParaTimes > executar
|
|
*/
|
|
export const executar = internalMutation({
|
|
args: {},
|
|
returns: v.object({
|
|
timesCreated: v.number(),
|
|
funcionariosAtribuidos: v.number(),
|
|
erros: v.array(v.string()),
|
|
}),
|
|
handler: async (ctx) => {
|
|
const erros: string[] = [];
|
|
let timesCreated = 0;
|
|
let funcionariosAtribuidos = 0;
|
|
|
|
try {
|
|
// 1. Buscar todos os funcionários que têm gestor definido
|
|
const funcionariosComGestor = await ctx.db
|
|
.query("funcionarios")
|
|
.filter((q) => q.neq(q.field("gestorId"), undefined))
|
|
.collect();
|
|
|
|
if (funcionariosComGestor.length === 0) {
|
|
return {
|
|
timesCreated: 0,
|
|
funcionariosAtribuidos: 0,
|
|
erros: ["Nenhum funcionário com gestor configurado encontrado"],
|
|
};
|
|
}
|
|
|
|
// 2. Agrupar funcionários por gestor
|
|
const gestoresMap = new Map<string, any[]>();
|
|
|
|
for (const funcionario of funcionariosComGestor) {
|
|
if (!funcionario.gestorId) continue;
|
|
|
|
const gestorId = funcionario.gestorId;
|
|
if (!gestoresMap.has(gestorId)) {
|
|
gestoresMap.set(gestorId, []);
|
|
}
|
|
gestoresMap.get(gestorId)!.push(funcionario);
|
|
}
|
|
|
|
// 3. Para cada gestor, criar um time
|
|
for (const [gestorId, subordinados] of gestoresMap.entries()) {
|
|
try {
|
|
const gestor = await ctx.db.get(gestorId as any);
|
|
|
|
if (!gestor) {
|
|
erros.push(`Gestor ${gestorId} não encontrado`);
|
|
continue;
|
|
}
|
|
|
|
// Verificar se já existe time para este gestor
|
|
const timeExistente = await ctx.db
|
|
.query("times")
|
|
.withIndex("by_gestor", (q) => q.eq("gestorId", gestorId as any))
|
|
.filter((q) => q.eq(q.field("ativo"), true))
|
|
.first();
|
|
|
|
let timeId;
|
|
|
|
if (timeExistente) {
|
|
timeId = timeExistente._id;
|
|
} else {
|
|
// Criar novo time
|
|
timeId = await ctx.db.insert("times", {
|
|
nome: `Equipe ${gestor.nome}`,
|
|
descricao: `Time gerenciado por ${gestor.nome} (migração automática)`,
|
|
gestorId: gestorId as any,
|
|
ativo: true,
|
|
cor: "#3B82F6",
|
|
});
|
|
timesCreated++;
|
|
}
|
|
|
|
// Adicionar membros ao time
|
|
for (const funcionario of subordinados) {
|
|
try {
|
|
// Verificar se já está em algum time
|
|
const membroExistente = await ctx.db
|
|
.query("timesMembros")
|
|
.withIndex("by_funcionario", (q) => q.eq("funcionarioId", funcionario._id))
|
|
.filter((q) => q.eq(q.field("ativo"), true))
|
|
.first();
|
|
|
|
if (!membroExistente) {
|
|
await ctx.db.insert("timesMembros", {
|
|
timeId: timeId,
|
|
funcionarioId: funcionario._id,
|
|
dataEntrada: Date.now(),
|
|
ativo: true,
|
|
});
|
|
funcionariosAtribuidos++;
|
|
}
|
|
} catch (e: any) {
|
|
erros.push(`Erro ao adicionar ${funcionario.nome} ao time: ${e.message}`);
|
|
}
|
|
}
|
|
} catch (e: any) {
|
|
erros.push(`Erro ao processar gestor ${gestorId}: ${e.message}`);
|
|
}
|
|
}
|
|
|
|
return {
|
|
timesCreated,
|
|
funcionariosAtribuidos,
|
|
erros,
|
|
};
|
|
} catch (e: any) {
|
|
erros.push(`Erro geral na migração: ${e.message}`);
|
|
return {
|
|
timesCreated,
|
|
funcionariosAtribuidos,
|
|
erros,
|
|
};
|
|
}
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Função auxiliar para limpar times inativos antigos
|
|
*/
|
|
export const limparTimesInativos = internalMutation({
|
|
args: {
|
|
diasInativos: v.optional(v.number()),
|
|
},
|
|
returns: v.number(),
|
|
handler: async (ctx, args) => {
|
|
const diasLimite = args.diasInativos || 30;
|
|
const dataLimite = Date.now() - (diasLimite * 24 * 60 * 60 * 1000);
|
|
|
|
const timesInativos = await ctx.db
|
|
.query("times")
|
|
.filter((q) => q.eq(q.field("ativo"), false))
|
|
.collect();
|
|
|
|
let removidos = 0;
|
|
|
|
for (const time of timesInativos) {
|
|
if (time._creationTime < dataLimite) {
|
|
// Remover membros inativos do time
|
|
const membrosInativos = await ctx.db
|
|
.query("timesMembros")
|
|
.withIndex("by_time", (q) => q.eq("timeId", time._id))
|
|
.filter((q) => q.eq(q.field("ativo"), false))
|
|
.collect();
|
|
|
|
for (const membro of membrosInativos) {
|
|
await ctx.db.delete(membro._id);
|
|
}
|
|
|
|
// Remover o time
|
|
await ctx.db.delete(time._id);
|
|
removidos++;
|
|
}
|
|
}
|
|
|
|
return removidos;
|
|
},
|
|
});
|
|
|