Feat controle ponto #29
@@ -238,7 +238,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="modal modal-open" style="display: flex; align-items: center; justify-content: center;">
|
<div class="modal modal-open" style="display: flex; align-items: center; justify-content: center;">
|
||||||
<div class="modal-box max-w-2xl w-[95%] max-h-[90vh] overflow-hidden flex flex-col" style="margin: auto;">
|
<div class="modal-box max-w-2xl w-[95%] max-h-[85vh] overflow-hidden flex flex-col" style="margin: auto; max-height: 85vh;">
|
||||||
<!-- Header fixo -->
|
<!-- Header fixo -->
|
||||||
<div class="flex items-center justify-between mb-4 pb-4 border-b border-base-300 flex-shrink-0">
|
<div class="flex items-center justify-between mb-4 pb-4 border-b border-base-300 flex-shrink-0">
|
||||||
<h3 class="font-bold text-lg">Comprovante de Registro de Ponto</h3>
|
<h3 class="font-bold text-lg">Comprovante de Registro de Ponto</h3>
|
||||||
@@ -312,7 +312,7 @@
|
|||||||
<img
|
<img
|
||||||
src={registro.imagemUrl}
|
src={registro.imagemUrl}
|
||||||
alt="Foto do registro de ponto"
|
alt="Foto do registro de ponto"
|
||||||
class="max-w-full max-h-96 rounded-lg border-2 border-primary object-contain"
|
class="max-w-full max-h-[250px] rounded-lg border-2 border-primary object-contain"
|
||||||
onerror={(e) => {
|
onerror={(e) => {
|
||||||
console.error('Erro ao carregar imagem:', e);
|
console.error('Erro ao carregar imagem:', e);
|
||||||
(e.target as HTMLImageElement).style.display = 'none';
|
(e.target as HTMLImageElement).style.display = 'none';
|
||||||
|
|||||||
@@ -874,7 +874,7 @@
|
|||||||
<!-- Modal de Confirmação -->
|
<!-- Modal de Confirmação -->
|
||||||
{#if mostrandoModalConfirmacao && imagemCapturada && dataHoraAtual}
|
{#if mostrandoModalConfirmacao && imagemCapturada && dataHoraAtual}
|
||||||
<div class="modal modal-open" style="display: flex; align-items: center; justify-content: center;">
|
<div class="modal modal-open" style="display: flex; align-items: center; justify-content: center;">
|
||||||
<div class="modal-box max-w-3xl w-[95%] max-h-[90vh] overflow-hidden flex flex-col" style="margin: auto;">
|
<div class="modal-box max-w-3xl w-[95%] max-h-[85vh] overflow-hidden flex flex-col" style="margin: auto; max-height: 85vh;">
|
||||||
<!-- Header fixo -->
|
<!-- Header fixo -->
|
||||||
<div class="flex items-center justify-between mb-6 pb-4 border-b border-base-300 flex-shrink-0">
|
<div class="flex items-center justify-between mb-6 pb-4 border-b border-base-300 flex-shrink-0">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
@@ -923,7 +923,7 @@
|
|||||||
<img
|
<img
|
||||||
src={URL.createObjectURL(imagemCapturada)}
|
src={URL.createObjectURL(imagemCapturada)}
|
||||||
alt="Foto capturada do registro de ponto"
|
alt="Foto capturada do registro de ponto"
|
||||||
class="max-w-full max-h-[400px] rounded-lg shadow-md object-contain"
|
class="max-w-full max-h-[250px] rounded-lg shadow-md object-contain"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -163,6 +163,9 @@
|
|||||||
r.dentroDoPrazo ? 'Sim' : 'Não',
|
r.dentroDoPrazo ? 'Sim' : 'Não',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Salvar a posição Y antes da tabela
|
||||||
|
const yPosAntesTabela = yPosition;
|
||||||
|
|
||||||
autoTable(doc, {
|
autoTable(doc, {
|
||||||
startY: yPosition,
|
startY: yPosition,
|
||||||
head: [['Data', 'Tipo', 'Horário', 'Dentro do Prazo']],
|
head: [['Data', 'Tipo', 'Horário', 'Dentro do Prazo']],
|
||||||
@@ -172,6 +175,91 @@
|
|||||||
styles: { fontSize: 9 },
|
styles: { fontSize: 9 },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Obter banco de horas do funcionário
|
||||||
|
const bancoHoras = await client.query(api.pontos.obterBancoHorasFuncionario, {
|
||||||
|
funcionarioId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calcular posição Y após a tabela
|
||||||
|
// autoTable armazena a posição final em doc.lastAutoTable.finalY
|
||||||
|
const lastPage = doc.getNumberOfPages();
|
||||||
|
doc.setPage(lastPage);
|
||||||
|
const finalY = (doc as any).lastAutoTable?.finalY;
|
||||||
|
|
||||||
|
// Se não conseguir obter a posição final, estimar baseado no número de linhas
|
||||||
|
if (finalY) {
|
||||||
|
yPosition = finalY;
|
||||||
|
} else {
|
||||||
|
// Estimativa: cada linha da tabela ocupa aproximadamente 7mm
|
||||||
|
const linhasTabela = tableData.length + 1; // +1 para o cabeçalho
|
||||||
|
yPosition = yPosAntesTabela + (linhasTabela * 7) + 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adicionar espaço antes do resumo
|
||||||
|
yPosition += 10;
|
||||||
|
|
||||||
|
// Verificar se precisa de nova página
|
||||||
|
if (yPosition > doc.internal.pageSize.getHeight() - 60) {
|
||||||
|
doc.addPage();
|
||||||
|
yPosition = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resumo do Banco de Horas
|
||||||
|
doc.setFontSize(12);
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.setTextColor(41, 128, 185);
|
||||||
|
doc.text('RESUMO DO BANCO DE HORAS', 15, yPosition);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.setTextColor(0, 0, 0);
|
||||||
|
|
||||||
|
yPosition += 10;
|
||||||
|
doc.setFontSize(10);
|
||||||
|
|
||||||
|
if (bancoHoras) {
|
||||||
|
const saldoMinutos = bancoHoras.saldoAcumuladoMinutos;
|
||||||
|
const horas = Math.floor(Math.abs(saldoMinutos) / 60);
|
||||||
|
const minutos = Math.abs(saldoMinutos) % 60;
|
||||||
|
const sinal = saldoMinutos >= 0 ? '+' : '-';
|
||||||
|
const saldoFormatado = `${sinal}${horas}h ${minutos}min`;
|
||||||
|
|
||||||
|
// Saldo Atual
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Saldo Atual:', 15, yPosition);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(saldoFormatado, 60, yPosition);
|
||||||
|
yPosition += 8;
|
||||||
|
|
||||||
|
// Horas Excedentes (se positivo)
|
||||||
|
if (saldoMinutos > 0) {
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.setTextColor(0, 128, 0); // Verde
|
||||||
|
doc.text('Horas Excedentes:', 15, yPosition);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(`${horas}h ${minutos}min`, 75, yPosition);
|
||||||
|
doc.setTextColor(0, 0, 0);
|
||||||
|
yPosition += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horas a Pagar (se negativo)
|
||||||
|
if (saldoMinutos < 0) {
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.setTextColor(200, 0, 0); // Vermelho
|
||||||
|
doc.text('Horas a Pagar:', 15, yPosition);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(`${horas}h ${minutos}min`, 70, yPosition);
|
||||||
|
doc.setTextColor(0, 0, 0);
|
||||||
|
yPosition += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total de dias registrados
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Total de Dias com Registro:', 15, yPosition);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(`${bancoHoras.totalDias} dias`, 95, yPosition);
|
||||||
|
} else {
|
||||||
|
doc.text('Banco de horas não disponível', 15, yPosition);
|
||||||
|
}
|
||||||
|
|
||||||
// Rodapé
|
// Rodapé
|
||||||
const pageCount = doc.getNumberOfPages();
|
const pageCount = doc.getNumberOfPages();
|
||||||
for (let i = 1; i <= pageCount; i++) {
|
for (let i = 1; i <= pageCount; i++) {
|
||||||
|
|||||||
@@ -1408,6 +1408,8 @@ export default defineSchema({
|
|||||||
nomeSaidaAlmoco: v.optional(v.string()), // Padrão: "Saída 1"
|
nomeSaidaAlmoco: v.optional(v.string()), // Padrão: "Saída 1"
|
||||||
nomeRetornoAlmoco: v.optional(v.string()), // Padrão: "Entrada 2"
|
nomeRetornoAlmoco: v.optional(v.string()), // Padrão: "Entrada 2"
|
||||||
nomeSaida: v.optional(v.string()), // Padrão: "Saída 2"
|
nomeSaida: v.optional(v.string()), // Padrão: "Saída 2"
|
||||||
|
// Ajuste de fuso horário (GMT offset em horas)
|
||||||
|
gmtOffset: v.optional(v.number()), // Padrão: 0 (UTC)
|
||||||
ativo: v.boolean(),
|
ativo: v.boolean(),
|
||||||
atualizadoPor: v.id("usuarios"),
|
atualizadoPor: v.id("usuarios"),
|
||||||
atualizadoEm: v.number(),
|
atualizadoEm: v.number(),
|
||||||
|
|||||||
Reference in New Issue
Block a user