feat: implement vacation management system with request approval, notification handling, and employee training tracking; enhance UI components for improved user experience
This commit is contained in:
171
packages/backend/convex/migrarParaTimes.ts
Normal file
171
packages/backend/convex/migrarParaTimes.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
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;
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user