365 lines
8.7 KiB
TypeScript
365 lines
8.7 KiB
TypeScript
import { v } from 'convex/values';
|
|
import { mutation, query } from './_generated/server';
|
|
import { getCurrentUserFunction } from './auth';
|
|
|
|
// Query: Listar todos os times
|
|
// Tipo inferido automaticamente pelo Convex
|
|
export const listar = query({
|
|
args: {},
|
|
handler: async (ctx) => {
|
|
const times = await ctx.db.query('times').collect();
|
|
|
|
// Buscar gestor e contar membros de cada time
|
|
const timesComDetalhes = await Promise.all(
|
|
times.map(async (time) => {
|
|
const gestor = await ctx.db.get(time.gestorId);
|
|
const membrosAtivos = await ctx.db
|
|
.query('timesMembros')
|
|
.withIndex('by_time_and_ativo', (q) => q.eq('timeId', time._id).eq('ativo', true))
|
|
.collect();
|
|
|
|
return {
|
|
...time,
|
|
gestor,
|
|
totalMembros: membrosAtivos.length
|
|
};
|
|
})
|
|
);
|
|
|
|
return timesComDetalhes;
|
|
}
|
|
});
|
|
|
|
// Query: Obter time por ID com membros
|
|
// Tipo inferido automaticamente pelo Convex
|
|
export const obterPorId = query({
|
|
args: { id: v.id('times') },
|
|
handler: async (ctx, args) => {
|
|
const time = await ctx.db.get(args.id);
|
|
if (!time) return null;
|
|
|
|
const gestor = await ctx.db.get(time.gestorId);
|
|
const membrosRelacoes = await ctx.db
|
|
.query('timesMembros')
|
|
.withIndex('by_time_and_ativo', (q) => q.eq('timeId', args.id).eq('ativo', true))
|
|
.collect();
|
|
|
|
// Buscar dados completos dos membros
|
|
const membros = await Promise.all(
|
|
membrosRelacoes.map(async (rel) => {
|
|
const funcionario = await ctx.db.get(rel.funcionarioId);
|
|
return {
|
|
...rel,
|
|
funcionario
|
|
};
|
|
})
|
|
);
|
|
|
|
return {
|
|
...time,
|
|
gestor,
|
|
membros
|
|
};
|
|
}
|
|
});
|
|
|
|
// Query: Obter time do funcionário
|
|
// Tipo inferido automaticamente pelo Convex
|
|
export const obterTimeFuncionario = query({
|
|
args: { funcionarioId: v.id('funcionarios') },
|
|
handler: async (ctx, args) => {
|
|
const relacao = await ctx.db
|
|
.query('timesMembros')
|
|
.withIndex('by_funcionario', (q) => q.eq('funcionarioId', args.funcionarioId))
|
|
.filter((q) => q.eq(q.field('ativo'), true))
|
|
.first();
|
|
|
|
if (!relacao) return null;
|
|
|
|
const time = await ctx.db.get(relacao.timeId);
|
|
if (!time) return null;
|
|
|
|
const gestor = await ctx.db.get(time.gestorId);
|
|
|
|
return {
|
|
...time,
|
|
gestor
|
|
};
|
|
}
|
|
});
|
|
|
|
// Query: Obter times do gestor
|
|
// Tipo inferido automaticamente pelo Convex
|
|
export const listarPorGestor = query({
|
|
args: { gestorId: v.id('usuarios') },
|
|
handler: async (ctx, args) => {
|
|
const times = await ctx.db
|
|
.query('times')
|
|
.withIndex('by_gestor', (q) => q.eq('gestorId', args.gestorId))
|
|
.filter((q) => q.eq(q.field('ativo'), true))
|
|
.collect();
|
|
|
|
const timesComMembros = await Promise.all(
|
|
times.map(async (time) => {
|
|
const membrosRelacoes = await ctx.db
|
|
.query('timesMembros')
|
|
.withIndex('by_time_and_ativo', (q) => q.eq('timeId', time._id).eq('ativo', true))
|
|
.collect();
|
|
|
|
const membros = await Promise.all(
|
|
membrosRelacoes.map(async (rel) => {
|
|
const funcionario = await ctx.db.get(rel.funcionarioId);
|
|
return {
|
|
...rel,
|
|
funcionario
|
|
};
|
|
})
|
|
);
|
|
|
|
return {
|
|
...time,
|
|
membros
|
|
};
|
|
})
|
|
);
|
|
|
|
return timesComMembros;
|
|
}
|
|
});
|
|
|
|
export const listarSubordinadosDoGestorAtual = query({
|
|
args: {},
|
|
handler: async (ctx) => {
|
|
const usuario = await getCurrentUserFunction(ctx);
|
|
if (!usuario) {
|
|
return [];
|
|
}
|
|
|
|
const timesGestor = await ctx.db
|
|
.query('times')
|
|
.withIndex('by_gestor', (q) => q.eq('gestorId', usuario._id))
|
|
.collect();
|
|
|
|
const timesComoSuperior = await ctx.db
|
|
.query('times')
|
|
.withIndex('by_gestor_superior', (q) => q.eq('gestorSuperiorId', usuario._id))
|
|
.collect();
|
|
|
|
const timesMap = new Map(
|
|
[...timesGestor, ...timesComoSuperior]
|
|
.filter((time) => time.ativo)
|
|
.map((time) => [time._id, time])
|
|
);
|
|
|
|
const resultado = [];
|
|
|
|
for (const time of timesMap.values()) {
|
|
const membrosRelacoes = await ctx.db
|
|
.query('timesMembros')
|
|
.withIndex('by_time_and_ativo', (q) => q.eq('timeId', time._id).eq('ativo', true))
|
|
.collect();
|
|
|
|
const membros = [];
|
|
for (const rel of membrosRelacoes) {
|
|
const funcionario = await ctx.db.get(rel.funcionarioId);
|
|
if (funcionario) {
|
|
// Buscar foto do perfil do funcionário através do usuário associado
|
|
let fotoPerfilUrl: string | null = null;
|
|
const usuario = await ctx.db
|
|
.query('usuarios')
|
|
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', funcionario._id))
|
|
.first();
|
|
if (usuario?.fotoPerfil) {
|
|
fotoPerfilUrl = await ctx.storage.getUrl(usuario.fotoPerfil);
|
|
}
|
|
|
|
membros.push({
|
|
relacaoId: rel._id,
|
|
funcionario: {
|
|
...funcionario,
|
|
fotoPerfilUrl
|
|
},
|
|
dataEntrada: rel.dataEntrada
|
|
});
|
|
}
|
|
}
|
|
|
|
resultado.push({
|
|
...time,
|
|
membros
|
|
});
|
|
}
|
|
|
|
return resultado;
|
|
}
|
|
});
|
|
|
|
// Mutation: Criar time
|
|
export const criar = mutation({
|
|
args: {
|
|
nome: v.string(),
|
|
descricao: v.optional(v.string()),
|
|
gestorId: v.id('usuarios'),
|
|
gestorSuperiorId: v.optional(v.id('usuarios')),
|
|
cor: v.optional(v.string())
|
|
},
|
|
returns: v.id('times'),
|
|
handler: async (ctx, args) => {
|
|
const timeId = await ctx.db.insert('times', {
|
|
nome: args.nome,
|
|
descricao: args.descricao,
|
|
gestorId: args.gestorId,
|
|
gestorSuperiorId: args.gestorSuperiorId ?? args.gestorId,
|
|
ativo: true,
|
|
cor: args.cor || '#3B82F6'
|
|
});
|
|
|
|
return timeId;
|
|
}
|
|
});
|
|
|
|
// Mutation: Atualizar time
|
|
export const atualizar = mutation({
|
|
args: {
|
|
id: v.id('times'),
|
|
nome: v.string(),
|
|
descricao: v.optional(v.string()),
|
|
gestorId: v.id('usuarios'),
|
|
gestorSuperiorId: v.optional(v.id('usuarios')),
|
|
cor: v.optional(v.string())
|
|
},
|
|
returns: v.null(),
|
|
handler: async (ctx, args) => {
|
|
const { id, ...dados } = args;
|
|
await ctx.db.patch(id, {
|
|
...dados,
|
|
gestorSuperiorId: dados.gestorSuperiorId ?? dados.gestorId
|
|
});
|
|
return null;
|
|
}
|
|
});
|
|
|
|
// Mutation: Desativar time
|
|
export const desativar = mutation({
|
|
args: { id: v.id('times') },
|
|
returns: v.null(),
|
|
handler: async (ctx, args) => {
|
|
// Desativar o time
|
|
await ctx.db.patch(args.id, { ativo: false });
|
|
|
|
// Desativar todos os membros
|
|
const membros = await ctx.db
|
|
.query('timesMembros')
|
|
.withIndex('by_time_and_ativo', (q) => q.eq('timeId', args.id).eq('ativo', true))
|
|
.collect();
|
|
|
|
for (const membro of membros) {
|
|
await ctx.db.patch(membro._id, {
|
|
ativo: false,
|
|
dataSaida: Date.now()
|
|
});
|
|
|
|
await ctx.db.patch(membro.funcionarioId, { gestorId: undefined });
|
|
}
|
|
|
|
return null;
|
|
}
|
|
});
|
|
|
|
// Mutation: Adicionar membro ao time
|
|
export const adicionarMembro = mutation({
|
|
args: {
|
|
timeId: v.id('times'),
|
|
funcionarioId: v.id('funcionarios')
|
|
},
|
|
returns: v.id('timesMembros'),
|
|
handler: async (ctx, args) => {
|
|
// Verificar se já não está em outro time ativo
|
|
const membroExistente = await ctx.db
|
|
.query('timesMembros')
|
|
.withIndex('by_funcionario', (q) => q.eq('funcionarioId', args.funcionarioId))
|
|
.filter((q) => q.eq(q.field('ativo'), true))
|
|
.first();
|
|
|
|
if (membroExistente) {
|
|
throw new Error('Funcionário já está em um time ativo');
|
|
}
|
|
|
|
const time = await ctx.db.get(args.timeId);
|
|
if (!time || !time.ativo) {
|
|
throw new Error('Time inválido ou inativo');
|
|
}
|
|
|
|
const membroId = await ctx.db.insert('timesMembros', {
|
|
timeId: args.timeId,
|
|
funcionarioId: args.funcionarioId,
|
|
dataEntrada: Date.now(),
|
|
ativo: true
|
|
});
|
|
|
|
await ctx.db.patch(args.funcionarioId, { gestorId: time.gestorId });
|
|
|
|
return membroId;
|
|
}
|
|
});
|
|
|
|
// Mutation: Remover membro do time
|
|
export const removerMembro = mutation({
|
|
args: { membroId: v.id('timesMembros') },
|
|
returns: v.null(),
|
|
handler: async (ctx, args) => {
|
|
const membro = await ctx.db.get(args.membroId);
|
|
if (!membro) {
|
|
throw new Error('Membro não encontrado');
|
|
}
|
|
await ctx.db.patch(args.membroId, {
|
|
ativo: false,
|
|
dataSaida: Date.now()
|
|
});
|
|
|
|
await ctx.db.patch(membro.funcionarioId, { gestorId: undefined });
|
|
return null;
|
|
}
|
|
});
|
|
|
|
// Mutation: Transferir membro para outro time
|
|
export const transferirMembro = mutation({
|
|
args: {
|
|
funcionarioId: v.id('funcionarios'),
|
|
novoTimeId: v.id('times')
|
|
},
|
|
returns: v.null(),
|
|
handler: async (ctx, args) => {
|
|
// Desativar do time atual
|
|
const relacaoAtual = await ctx.db
|
|
.query('timesMembros')
|
|
.withIndex('by_funcionario', (q) => q.eq('funcionarioId', args.funcionarioId))
|
|
.filter((q) => q.eq(q.field('ativo'), true))
|
|
.first();
|
|
|
|
if (relacaoAtual) {
|
|
await ctx.db.patch(relacaoAtual._id, {
|
|
ativo: false,
|
|
dataSaida: Date.now()
|
|
});
|
|
}
|
|
|
|
// Adicionar ao novo time
|
|
const novoTime = await ctx.db.get(args.novoTimeId);
|
|
if (!novoTime || !novoTime.ativo) {
|
|
throw new Error('Novo time inválido ou inativo');
|
|
}
|
|
|
|
await ctx.db.insert('timesMembros', {
|
|
timeId: args.novoTimeId,
|
|
funcionarioId: args.funcionarioId,
|
|
dataEntrada: Date.now(),
|
|
ativo: true
|
|
});
|
|
|
|
await ctx.db.patch(args.funcionarioId, { gestorId: novoTime.gestorId });
|
|
|
|
return null;
|
|
}
|
|
});
|