feat: Add 'atas' (minutes/records) management feature, and implement various improvements across UI, backend logic, and authentication.

This commit is contained in:
2025-12-02 16:37:48 -03:00
parent 05e7f1181d
commit 4bd9e21748
265 changed files with 29156 additions and 26460 deletions

View File

@@ -1,121 +1,123 @@
import { internalMutation, query } from "./_generated/server";
import { v } from "convex/values";
import { Id, Doc } from "./_generated/dataModel";
import { v } from 'convex/values';
import type { Doc, Id } from './_generated/dataModel';
import { internalMutation, query } from './_generated/server';
/**
* Verificar duplicatas de matrícula (agora busca do funcionário associado)
*/
export const verificarDuplicatas = query({
args: {},
returns: v.array(
v.object({
matricula: v.string(),
count: v.number(),
usuarios: v.array(
v.object({
_id: v.id("usuarios"),
nome: v.string(),
email: v.string(),
})
),
})
),
handler: async (ctx) => {
const usuarios = await ctx.db.query("usuarios").collect();
// Agrupar por matrícula do funcionário associado
const gruposPorMatricula: Record<string, Array<{ _id: Id<"usuarios">; nome: string; email: string }>> = {};
for (const usuario of usuarios) {
let matricula: string | undefined = undefined;
if (usuario.funcionarioId) {
const funcionario = await ctx.db.get(usuario.funcionarioId);
matricula = funcionario?.matricula;
}
if (matricula) {
if (!gruposPorMatricula[matricula]) {
gruposPorMatricula[matricula] = [];
}
gruposPorMatricula[matricula].push({
_id: usuario._id,
nome: usuario.nome,
email: usuario.email || "",
});
}
}
// Filtrar apenas duplicatas
const duplicatas = Object.entries(gruposPorMatricula)
.filter(([_, usuarios]) => usuarios.length > 1)
.map(([matricula, usuarios]) => ({
matricula,
count: usuarios.length,
usuarios,
}));
return duplicatas;
},
args: {},
returns: v.array(
v.object({
matricula: v.string(),
count: v.number(),
usuarios: v.array(
v.object({
_id: v.id('usuarios'),
nome: v.string(),
email: v.string()
})
)
})
),
handler: async (ctx) => {
const usuarios = await ctx.db.query('usuarios').collect();
// Agrupar por matrícula do funcionário associado
const gruposPorMatricula: Record<
string,
Array<{ _id: Id<'usuarios'>; nome: string; email: string }>
> = {};
for (const usuario of usuarios) {
let matricula: string | undefined;
if (usuario.funcionarioId) {
const funcionario = await ctx.db.get(usuario.funcionarioId);
matricula = funcionario?.matricula;
}
if (matricula) {
if (!gruposPorMatricula[matricula]) {
gruposPorMatricula[matricula] = [];
}
gruposPorMatricula[matricula].push({
_id: usuario._id,
nome: usuario.nome,
email: usuario.email || ''
});
}
}
// Filtrar apenas duplicatas
const duplicatas = Object.entries(gruposPorMatricula)
.filter(([_, usuarios]) => usuarios.length > 1)
.map(([matricula, usuarios]) => ({
matricula,
count: usuarios.length,
usuarios
}));
return duplicatas;
}
});
/**
* Remover duplicatas mantendo apenas o mais recente (agora busca do funcionário associado)
*/
export const removerDuplicatas = internalMutation({
args: {},
returns: v.object({
removidos: v.number(),
matriculas: v.array(v.string()),
}),
handler: async (ctx) => {
const usuarios = await ctx.db.query("usuarios").collect();
// Agrupar por matrícula do funcionário associado
const gruposPorMatricula: Record<string, Doc<"usuarios">[]> = {};
for (const usuario of usuarios) {
let matricula: string | undefined = undefined;
if (usuario.funcionarioId) {
const funcionario = await ctx.db.get(usuario.funcionarioId);
matricula = funcionario?.matricula;
}
if (matricula) {
if (!gruposPorMatricula[matricula]) {
gruposPorMatricula[matricula] = [];
}
gruposPorMatricula[matricula].push(usuario);
}
}
let removidos = 0;
const matriculasDuplicadas: string[] = [];
// Para cada grupo com duplicatas
for (const [matricula, usuariosGrupo] of Object.entries(gruposPorMatricula)) {
if (usuariosGrupo.length > 1) {
matriculasDuplicadas.push(matricula);
// Ordenar por _creationTime (mais recente primeiro)
usuariosGrupo.sort((a, b) => b._creationTime - a._creationTime);
// Manter o primeiro (mais recente) e remover os outros
for (let i = 1; i < usuariosGrupo.length; i++) {
await ctx.db.delete(usuariosGrupo[i]._id);
removidos++;
console.log(`🗑️ Removido usuário duplicado: ${usuariosGrupo[i].nome} (matrícula: ${matricula})`);
}
console.log(`✅ Mantido usuário: ${usuariosGrupo[0].nome} (matrícula: ${matricula})`);
}
}
return {
removidos,
matriculas: matriculasDuplicadas,
};
},
args: {},
returns: v.object({
removidos: v.number(),
matriculas: v.array(v.string())
}),
handler: async (ctx) => {
const usuarios = await ctx.db.query('usuarios').collect();
// Agrupar por matrícula do funcionário associado
const gruposPorMatricula: Record<string, Doc<'usuarios'>[]> = {};
for (const usuario of usuarios) {
let matricula: string | undefined;
if (usuario.funcionarioId) {
const funcionario = await ctx.db.get(usuario.funcionarioId);
matricula = funcionario?.matricula;
}
if (matricula) {
if (!gruposPorMatricula[matricula]) {
gruposPorMatricula[matricula] = [];
}
gruposPorMatricula[matricula].push(usuario);
}
}
let removidos = 0;
const matriculasDuplicadas: string[] = [];
// Para cada grupo com duplicatas
for (const [matricula, usuariosGrupo] of Object.entries(gruposPorMatricula)) {
if (usuariosGrupo.length > 1) {
matriculasDuplicadas.push(matricula);
// Ordenar por _creationTime (mais recente primeiro)
usuariosGrupo.sort((a, b) => b._creationTime - a._creationTime);
// Manter o primeiro (mais recente) e remover os outros
for (let i = 1; i < usuariosGrupo.length; i++) {
await ctx.db.delete(usuariosGrupo[i]._id);
removidos++;
console.log(
`🗑️ Removido usuário duplicado: ${usuariosGrupo[i].nome} (matrícula: ${matricula})`
);
}
console.log(`✅ Mantido usuário: ${usuariosGrupo[0].nome} (matrícula: ${matricula})`);
}
}
return {
removidos,
matriculas: matriculasDuplicadas
};
}
});