<!--
Calculadora de Horas Trabalhadas
Modelo pronto para colar em um post ou página do Blogger.
Inclui:
- Adição de entradas (data, início, fim, intervalo em minutos)
- Cálculo automático de duração por entrada
- Soma total de horas e minutos
- Cálculo de horas extras com limite configurável
- Exportar em CSV e imprimir
- Comentário onde inserir o código do AdSense (placeholder)
Instruções de uso no Blogger:
1. No painel do Blogger, crie uma nova página ou post.
2. Mude para a aba "HTML" (não Compose) e cole todo esse código.
3. Publique.
Observação: para o AdSense, cole o código do anúncio no local indicado entre os comentários.
-->
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Calculadora de Horas Trabalhadas</title>
<style>
:root{--bg:#0f1724;--card:#0b1220;--accent:#1e90ff;--muted:#94a3b8;--glass: rgba(255,255,255,0.03)}
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,'Helvetica Neue',Arial; background:linear-gradient(180deg,#071028 0%, #071023 100%); color:#e6eef8; margin:0; padding:24px}
.container{max-width:980px;margin:0 auto}
.card{background:linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0.02)); padding:18px;border-radius:12px;box-shadow:0 6px 18px rgba(2,6,23,0.6);}
h1{margin:0 0 8px;font-size:22px}
p.lead{color:var(--muted);margin-top:0}
.controls{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}
.controls > *{flex:1;min-width:160px}
input,select,button{padding:8px 10px;border-radius:8px;border:1px solid rgba(255,255,255,0.04);background:transparent;color:inherit}
table{width:100%;border-collapse:collapse;margin-top:12px}
th,td{padding:8px 6px;text-align:left;border-bottom:1px dashed rgba(255,255,255,0.03)}
th{color:var(--muted);font-weight:600;font-size:13px}
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
.btn{background:var(--accent);border:none;color:#002038;padding:8px 12px;border-radius:8px;cursor:pointer;font-weight:600}
.btn.ghost{background:transparent;border:1px solid rgba(255,255,255,0.04);color:var(--muted)}
.small{font-size:13px;color:var(--muted)}
.result{margin-top:12px;padding:12px;border-radius:10px;background:var(--glass);display:flex;justify-content:space-between;align-items:center}
.result .big{font-size:20px;font-weight:700}
.right{display:flex;gap:12px;align-items:center}
.note{font-size:13px;color:var(--muted);margin-top:8px}
@media(max-width:720px){.controls{flex-direction:column}.right{flex-direction:column;align-items:flex-end}}
</style>
</head>
<body>
<div class="container">
<div class="card">
<h1>Calculadora de Horas Trabalhadas</h1>
<p class="lead">Adicione entradas de jornada (data, início, fim e intervalo). A calculadora soma o total, mostra horas extras e permite exportar.</p>
<div class="controls">
<input id="date" type="date" />
<input id="start" type="time" />
<input id="end" type="time" />
<input id="break" type="number" min="0" placeholder="Intervalo (min)" />
<button id="addRow" class="btn">Adicionar</button>
</div>
<div class="controls">
<label class="small">Limite Diário p/ jornada padrão (horas)</label>
<input id="dailyLimit" type="number" min="0" step="0.25" value="8" />
<label class="small">Hora extra começa após (por dia)</label>
<input id="overtimeRate" type="number" min="0" step="0.25" value="8" />
<label class="small">Formato de exibição</label>
<select id="format">
<option value="decimal">Horas decimais (ex: 8.50)</option>
<option value="hms">Horas e minutos (ex: 8h 30m)</option>
</select>
</div>
<!-- Placeholder para o AdSense: cole seu código de anúncio aqui (apenas após aprovação do site no AdSense) -->
<!-- EX: <!-- AD CODE HERE --> -->
<table id="table">
<thead>
<tr>
<th>Data</th>
<th>Início</th>
<th>Fim</th>
<th>Intervalo (min)</th>
<th>Duração</th>
<th>Ações</th>
</tr>
</thead>
<tbody></tbody>
</table>
<div class="actions">
<button id="clearAll" class="btn ghost">Limpar tudo</button>
<button id="exportCsv" class="btn">Exportar CSV</button>
<button id="print" class="btn">Imprimir</button>
</div>
<div class="result" id="resultBox">
<div>
<div class="big" id="totalHours">Total: 0h 0m</div>
<div class="small" id="totalDecimal">(0.00 horas)</div>
</div>
<div class="right">
<div class="small">Horas extras: <span id="overtime">0h 0m</span></div>
<div class="small">Dias calculados: <span id="daysCount">0</span></div>
</div>
</div>
<p class="note">Dica: use o botão "Exportar CSV" para importar os registros em planilhas. Se quiser, ajuste o limite de jornada para calcular horas extras automaticamente.</p>
</div>
</div>
<script>
// Utilitários
const $ = sel => document.querySelector(sel);
const tbody = document.querySelector('#table tbody');
const rows = [];
function parseTimeToMinutes(timeStr){
if(!timeStr) return null;
const [h,m] = timeStr.split(':').map(Number);
if(Number.isNaN(h) || Number.isNaN(m)) return null;
return h*60 + m;
}
function minutesToHms(minutes){
const sign = minutes<0?'-':'';
minutes = Math.abs(Math.round(minutes));
const h = Math.floor(minutes/60);
const m = minutes%60;
return `${sign}${h}h ${m}m`;
}
function minutesToDecimal(minutes){
const dec = (minutes/60);
return dec.toFixed(2);
}
function computeDuration(start, end, brk){
// start and end in mm since midnight
if(start==null || end==null) return 0;
let dur = end - start;
if(dur < 0) dur += 24*60; // passada de meia-noite
dur -= (brk||0);
if(dur < 0) dur = 0;
return dur;
}
function renderRows(){
tbody.innerHTML = '';
rows.forEach((r, idx) => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${r.date}</td>
<td>${r.start}</td>
<td>${r.end}</td>
<td>${r.break}</td>
<td>${r.display}</td>
<td><button data-idx="${idx}" class="btn ghost btnDel">Excluir</button></td>
`;
tbody.appendChild(tr);
});
updateSummary();
}
function updateSummary(){
let totalMin = 0;
const dailyLimit = parseFloat(document.getElementById('overtimeRate').value) || 8;
const format = document.getElementById('format').value;
const byDay = {};
rows.forEach(r => {
totalMin += r.minutes;
if(!byDay[r.date]) byDay[r.date] = 0;
byDay[r.date] += r.minutes;
});
let overtimeMin = 0;
Object.values(byDay).forEach(mins => {
const limitMin = dailyLimit*60;
if(mins > limitMin) overtimeMin += (mins - limitMin);
});
document.getElementById('daysCount').textContent = Object.keys(byDay).length;
document.getElementById('overtime').textContent = minutesToHms(overtimeMin);
if(format === 'hms'){
document.getElementById('totalHours').textContent = `Total: ${minutesToHms(totalMin)}`;
document.getElementById('totalDecimal').textContent = `(${minutesToDecimal(totalMin)} horas)`;
} else {
document.getElementById('totalHours').textContent = `Total: ${minutesToDecimal(totalMin)} horas`;
document.getElementById('totalDecimal').textContent = `(${minutesToHms(totalMin)})`;
}
}
// Eventos
$('#addRow').addEventListener('click', ()=>{
const date = $('#date').value || new Date().toISOString().slice(0,10);
const start = $('#start').value;
const end = $('#end').value;
const brk = parseInt($('#break').value) || 0;
const s = parseTimeToMinutes(start);
const e = parseTimeToMinutes(end);
if(s==null || e==null){
alert('Por favor, preencha início e fim corretamente.');
return;
}
const mins = computeDuration(s,e,brk);
const display = minutesToHms(mins);
rows.push({date, start, end, break:brk, minutes:mins, display});
renderRows();
// limpa inputs
$('#start').value = '';
$('#end').value = '';
$('#break').value = '';
});
tbody.addEventListener('click', (ev)=>{
if(ev.target.classList.contains('btnDel')){
const idx = Number(ev.target.dataset.idx);
rows.splice(idx,1);
renderRows();
}
});
$('#clearAll').addEventListener('click', ()=>{
if(confirm('Remover todas as entradas?')){
rows.length = 0;
renderRows();
}
});
$('#exportCsv').addEventListener('click', ()=>{
if(rows.length === 0){ alert('Nenhuma entrada para exportar.'); return; }
const header = ['Data','Inicio','Fim','Intervalo(min)','Duracao(min)','Duracao(exibicao)'];
const lines = [header.join(',')];
rows.forEach(r => {
lines.push([r.date, r.start, r.end, r.break, r.minutes, r.display].join(','));
});
const csv = lines.join('\n');
const blob = new Blob([csv], {type:'text/csv;charset=utf-8;'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'horas_trabalhadas.csv';
a.click();
URL.revokeObjectURL(url);
});
$('#print').addEventListener('click', ()=>{
window.print();
});
// configuração inicial: teste de exibição
updateSummary();
</script>
</body>
</html>