|
|
|
@ -1,3 +1,15 @@ |
|
|
|
<div class="mb-4"> |
|
|
|
<label for="minute-inputs" class="form-label">Minuteneinträge (z. B. 45, 60, 30):</label> |
|
|
|
<textarea id="minute-inputs" class="form-control" rows="3"></textarea> |
|
|
|
<button id="sum-button" class="btn btn-primary mt-2">Summieren</button> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="mb-4"> |
|
|
|
<p>Gesamtsumme: <strong><span id="total-time">0h 0min</span></strong> (<span id="total-minutes">0min</span>)</p> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<%= form_with(model: entry, local: true) do |form| %> |
|
|
|
<% if entry.errors.any? %> |
|
|
|
<div class="alert alert-danger"> |
|
|
|
@ -10,6 +22,7 @@ |
|
|
|
</div> |
|
|
|
<% end %> |
|
|
|
|
|
|
|
|
|
|
|
<!-- Erste Zeile: Datum, Stunden, Minuten --> |
|
|
|
<div class="row g-3 mt-1"> |
|
|
|
<div class="col-12 col-md-4"> |
|
|
|
@ -27,6 +40,21 @@ |
|
|
|
value: (form.object.date) %> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="col-12 col-md-4"> |
|
|
|
<%= form.label :start_time, 'Beginn (Uhrzeit)', class: 'form-label' %> |
|
|
|
<%= form.time_field :start_time, class: 'form-control', step: 60 %> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="col-12 col-md-4"> |
|
|
|
<%= form.label :end_time, 'Ende (Uhrzeit)', class: 'form-label' %> |
|
|
|
<%= form.time_field :end_time, class: 'form-control', step: 60 %> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="col-12 col-md-4"> |
|
|
|
<%= form.label :hours, 'Stunden', class: 'form-label' %> |
|
|
|
<%= form.number_field :hours, |
|
|
|
@ -42,6 +70,22 @@ |
|
|
|
min: 0, max: 59, |
|
|
|
value: form.object.minutes || 0 %> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="col-12 col-md-4"> |
|
|
|
<div class="col-12 mt-2 d-flex justify-content-center"> |
|
|
|
<div class="form-check mt-4"> |
|
|
|
<%= form.check_box :has_break, { class: 'form-check-input', id: 'entry_has_break' }, true, false %> |
|
|
|
<%= form.label :has_break, '30 Min. Mittagspause abziehen', class: 'form-check-label ms-2' %> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="col-12 col-md-4 d-flex align-items-end"> |
|
|
|
<p class="mb-0"><strong>Gesamt:</strong> <span id="calculated-total-minutes">0 min</span></p> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- Weitere Felder: Typ, Art, Entfernung --> |
|
|
|
@ -99,3 +143,97 @@ |
|
|
|
} |
|
|
|
}); |
|
|
|
</script> |
|
|
|
<script> |
|
|
|
document.addEventListener("DOMContentLoaded", function () { |
|
|
|
const textarea = document.getElementById("minute-inputs"); |
|
|
|
const totalSpan = document.getElementById("total-time"); |
|
|
|
const totalMinutesSpan = document.getElementById("total-minutes"); |
|
|
|
const totalMinutesDisplay = document.getElementById("calculated-total-minutes"); |
|
|
|
const sumBtn = document.getElementById("sum-button"); |
|
|
|
|
|
|
|
const startInput = document.getElementById("entry_start_time"); |
|
|
|
const endInput = document.getElementById("entry_end_time"); |
|
|
|
const hourInput = document.getElementById("entry_hours"); |
|
|
|
const minuteInput = document.getElementById("entry_minutes"); |
|
|
|
const breakCheckbox = document.getElementById("entry_has_break"); |
|
|
|
|
|
|
|
let baseTotalMinutes = 0; // <- diese merken wir uns immer ohne Pause-Abzug! |
|
|
|
|
|
|
|
function updateDisplayFromBaseMinutes() { |
|
|
|
let total = baseTotalMinutes; |
|
|
|
if (breakCheckbox && breakCheckbox.checked) total = Math.max(0, total - 30); |
|
|
|
|
|
|
|
const h = Math.floor(total / 60); |
|
|
|
const m = total % 60; |
|
|
|
|
|
|
|
hourInput.value = h; |
|
|
|
minuteInput.value = m; |
|
|
|
|
|
|
|
totalSpan.textContent = `${h}h ${m}min`; |
|
|
|
totalMinutesSpan.textContent = `${total} min`; |
|
|
|
totalMinutesDisplay.textContent = `${total} min`; |
|
|
|
} |
|
|
|
|
|
|
|
function clearFields(exclude = null) { |
|
|
|
if (exclude !== 'textarea') textarea.value = ''; |
|
|
|
if (exclude !== 'time') { |
|
|
|
startInput.value = ''; |
|
|
|
endInput.value = ''; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
sumBtn.addEventListener("click", function () { |
|
|
|
const values = textarea.value.split(/[ ,;\n]+/).map(v => parseInt(v.trim())).filter(Number.isFinite); |
|
|
|
baseTotalMinutes = values.reduce((sum, val) => sum + val, 0); |
|
|
|
updateDisplayFromBaseMinutes(); |
|
|
|
clearFields('textarea'); |
|
|
|
}); |
|
|
|
|
|
|
|
function calculateFromTimes() { |
|
|
|
const start = startInput.value; |
|
|
|
const end = endInput.value; |
|
|
|
|
|
|
|
if (start && end) { |
|
|
|
const [sh, sm] = start.split(':').map(Number); |
|
|
|
const [eh, em] = end.split(':').map(Number); |
|
|
|
|
|
|
|
let startDate = new Date(0, 0, 0, sh, sm); |
|
|
|
let endDate = new Date(0, 0, 0, eh, em); |
|
|
|
|
|
|
|
let diffMin = (endDate - startDate) / 60000; |
|
|
|
if (diffMin < 0) diffMin += 1440; |
|
|
|
|
|
|
|
baseTotalMinutes = diffMin; |
|
|
|
updateDisplayFromBaseMinutes(); |
|
|
|
clearFields('time'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function normalizeManualMinutes() { |
|
|
|
let minutes = parseInt(minuteInput.value, 10); |
|
|
|
if (!isNaN(minutes) && minutes >= 60) { |
|
|
|
const extra = Math.floor(minutes / 60); |
|
|
|
const remain = minutes % 60; |
|
|
|
const currentHours = parseInt(hourInput.value, 10) || 0; |
|
|
|
hourInput.value = currentHours + extra; |
|
|
|
minuteInput.value = remain; |
|
|
|
} |
|
|
|
baseTotalMinutes = (parseInt(hourInput.value, 10) || 0) * 60 + (parseInt(minuteInput.value, 10) || 0); |
|
|
|
updateDisplayFromBaseMinutes(); |
|
|
|
clearFields(); // alle Quellen löschen |
|
|
|
} |
|
|
|
|
|
|
|
// Listeners |
|
|
|
if (startInput && endInput) { |
|
|
|
startInput.addEventListener("change", calculateFromTimes); |
|
|
|
endInput.addEventListener("change", calculateFromTimes); |
|
|
|
} |
|
|
|
|
|
|
|
if (hourInput) hourInput.addEventListener("input", normalizeManualMinutes); |
|
|
|
if (minuteInput) minuteInput.addEventListener("blur", normalizeManualMinutes); |
|
|
|
|
|
|
|
if (breakCheckbox) { |
|
|
|
breakCheckbox.addEventListener("change", updateDisplayFromBaseMinutes); |
|
|
|
} |
|
|
|
}); |
|
|
|
</script> |