- Added functionality to customize labels for point registration types (Entrada, Saída, etc.) in the configuration settings. - Introduced a GMT offset adjustment feature to account for time zone differences during point registration. - Updated the backend to ensure default values for custom labels and GMT offset are set correctly. - Enhanced the UI to allow users to input and save personalized names for each type of point registration. - Improved the point registration process to utilize the new configuration settings for displaying labels consistently across the application.
146 lines
4.6 KiB
TypeScript
146 lines
4.6 KiB
TypeScript
import { v } from 'convex/values';
|
|
import { mutation, query } from './_generated/server';
|
|
import { getCurrentUserFunction } from './auth';
|
|
import type { Id } from './_generated/dataModel';
|
|
|
|
/**
|
|
* Valida formato de horário HH:mm
|
|
*/
|
|
function validarHorario(horario: string): boolean {
|
|
const regex = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/;
|
|
return regex.test(horario);
|
|
}
|
|
|
|
/**
|
|
* Obtém a configuração ativa de ponto
|
|
*/
|
|
export const obterConfiguracao = query({
|
|
args: {},
|
|
handler: async (ctx) => {
|
|
const config = await ctx.db
|
|
.query('configuracaoPonto')
|
|
.withIndex('by_ativo', (q) => q.eq('ativo', true))
|
|
.first();
|
|
|
|
if (!config) {
|
|
// Retornar configuração padrão se não houver
|
|
return {
|
|
horarioEntrada: '08:00',
|
|
horarioSaidaAlmoco: '12:00',
|
|
horarioRetornoAlmoco: '13:00',
|
|
horarioSaida: '17:00',
|
|
toleranciaMinutos: 15,
|
|
nomeEntrada: 'Entrada 1',
|
|
nomeSaidaAlmoco: 'Saída 1',
|
|
nomeRetornoAlmoco: 'Entrada 2',
|
|
nomeSaida: 'Saída 2',
|
|
ativo: false,
|
|
};
|
|
}
|
|
|
|
// Garantir que os nomes padrão estejam definidos
|
|
return {
|
|
...config,
|
|
nomeEntrada: config.nomeEntrada || 'Entrada 1',
|
|
nomeSaidaAlmoco: config.nomeSaidaAlmoco || 'Saída 1',
|
|
nomeRetornoAlmoco: config.nomeRetornoAlmoco || 'Entrada 2',
|
|
nomeSaida: config.nomeSaida || 'Saída 2',
|
|
};
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Salva configuração de ponto (apenas TI)
|
|
*/
|
|
export const salvarConfiguracao = mutation({
|
|
args: {
|
|
horarioEntrada: v.string(),
|
|
horarioSaidaAlmoco: v.string(),
|
|
horarioRetornoAlmoco: v.string(),
|
|
horarioSaida: v.string(),
|
|
toleranciaMinutos: v.number(),
|
|
nomeEntrada: v.optional(v.string()),
|
|
nomeSaidaAlmoco: v.optional(v.string()),
|
|
nomeRetornoAlmoco: v.optional(v.string()),
|
|
nomeSaida: v.optional(v.string()),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const usuario = await getCurrentUserFunction(ctx);
|
|
if (!usuario) {
|
|
throw new Error('Usuário não autenticado');
|
|
}
|
|
|
|
// TODO: Verificar se usuário tem permissão de TI
|
|
// Por enquanto, permitir se tiver roleId
|
|
|
|
// Validar horários
|
|
if (!validarHorario(args.horarioEntrada)) {
|
|
throw new Error('Horário de entrada inválido (formato: HH:mm)');
|
|
}
|
|
if (!validarHorario(args.horarioSaidaAlmoco)) {
|
|
throw new Error('Horário de saída para almoço inválido (formato: HH:mm)');
|
|
}
|
|
if (!validarHorario(args.horarioRetornoAlmoco)) {
|
|
throw new Error('Horário de retorno do almoço inválido (formato: HH:mm)');
|
|
}
|
|
if (!validarHorario(args.horarioSaida)) {
|
|
throw new Error('Horário de saída inválido (formato: HH:mm)');
|
|
}
|
|
|
|
// Validar tolerância
|
|
if (args.toleranciaMinutos < 0 || args.toleranciaMinutos > 60) {
|
|
throw new Error('Tolerância deve estar entre 0 e 60 minutos');
|
|
}
|
|
|
|
// Validar sequência lógica de horários
|
|
const [horaEntrada, minutoEntrada] = args.horarioEntrada.split(':').map(Number);
|
|
const [horaSaidaAlmoco, minutoSaidaAlmoco] = args.horarioSaidaAlmoco.split(':').map(Number);
|
|
const [horaRetornoAlmoco, minutoRetornoAlmoco] = args.horarioRetornoAlmoco.split(':').map(Number);
|
|
const [horaSaida, minutoSaida] = args.horarioSaida.split(':').map(Number);
|
|
|
|
const minutosEntrada = horaEntrada * 60 + minutoEntrada;
|
|
const minutosSaidaAlmoco = horaSaidaAlmoco * 60 + minutoSaidaAlmoco;
|
|
const minutosRetornoAlmoco = horaRetornoAlmoco * 60 + minutoRetornoAlmoco;
|
|
const minutosSaida = horaSaida * 60 + minutoSaida;
|
|
|
|
if (minutosEntrada >= minutosSaidaAlmoco) {
|
|
throw new Error('Horário de entrada deve ser anterior à saída para almoço');
|
|
}
|
|
if (minutosSaidaAlmoco >= minutosRetornoAlmoco) {
|
|
throw new Error('Horário de saída para almoço deve ser anterior ao retorno');
|
|
}
|
|
if (minutosRetornoAlmoco >= minutosSaida) {
|
|
throw new Error('Horário de retorno do almoço deve ser anterior à saída');
|
|
}
|
|
|
|
// Desativar configurações antigas
|
|
const configsAntigas = await ctx.db
|
|
.query('configuracaoPonto')
|
|
.withIndex('by_ativo', (q) => q.eq('ativo', true))
|
|
.collect();
|
|
|
|
for (const configAntiga of configsAntigas) {
|
|
await ctx.db.patch(configAntiga._id, { ativo: false });
|
|
}
|
|
|
|
// Criar nova configuração
|
|
const configId = await ctx.db.insert('configuracaoPonto', {
|
|
horarioEntrada: args.horarioEntrada,
|
|
horarioSaidaAlmoco: args.horarioSaidaAlmoco,
|
|
horarioRetornoAlmoco: args.horarioRetornoAlmoco,
|
|
horarioSaida: args.horarioSaida,
|
|
toleranciaMinutos: args.toleranciaMinutos,
|
|
nomeEntrada: args.nomeEntrada || 'Entrada 1',
|
|
nomeSaidaAlmoco: args.nomeSaidaAlmoco || 'Saída 1',
|
|
nomeRetornoAlmoco: args.nomeRetornoAlmoco || 'Entrada 2',
|
|
nomeSaida: args.nomeSaida || 'Saída 2',
|
|
ativo: true,
|
|
atualizadoPor: usuario._id as Id<'usuarios'>,
|
|
atualizadoEm: Date.now(),
|
|
});
|
|
|
|
return { configId };
|
|
},
|
|
});
|
|
|