Browse Source

fix view

main
Christoph Marzell 5 days ago
parent
commit
d4cf253342
  1. 226
      app/views/entries/index.html.erb

226
app/views/entries/index.html.erb

@ -30,7 +30,7 @@
id="showAllCombinations"
>
<label class="form-check-label" for="showAllCombinations">
Alle anzeigen
Erledigte anzeigen
</label>
</div>
</div>
@ -58,82 +58,69 @@
<% Entry::PRAKTIKUMSTYPEN.each do |typ| %>
<% Entry::ENTRY_ARTEN.each do |art| %>
<% remaining = @remaining_minutes_matrix.dig(typ, art) %>
<% spent = @spent_minutes_matrix.dig(typ, art) %>
<% soll = current_user.required_hours_matrix.dig(typ, art) %>
<% weekly = current_user.weekly_target_matrix.dig(typ, art) %>
<% remaining = @remaining_minutes_matrix.dig(typ, art).to_i %>
<% spent = @spent_minutes_matrix.dig(typ, art).to_i %>
<% soll = current_user.required_hours_for(typ, art).to_f %>
<% weekly = current_user.weekly_target_for(typ, art).to_f %>
<% ende = @estimated_end_by_typ_art.dig(typ, art) %>
<% next if spent.blank? %>
<% next if soll.blank? %>
<%
# Standardmäßig zeigen wir nur Einträge,
# Ohne Sollwert und ohne geleistete Zeit ist die Kombination
# für die Übersicht nicht relevant.
next if soll.zero? && spent.zero?
# Standardmäßig nur Anforderungen anzeigen,
# bei denen tatsächlich noch etwas offen ist.
has_remaining = remaining.to_i.positive?
has_remaining = remaining.positive?
%>
<% if remaining.present? || soll.present? || weekly.present? %>
<tr
data-has-remaining="<%= has_remaining %>"
class="<%= 'd-none' unless has_remaining %>"
>
<td><%= typ.capitalize %></td>
<td><%= art.capitalize %></td>
<td>
<% if remaining.present? %>
<%= remaining / 60 %> h <%= remaining % 60 %> min
<% else %>
<% end %>
</td>
<td>
<% if spent.present? %>
<%= spent / 60 %> h <%= spent % 60 %> min
<% else %>
<% end %>
</td>
<td>
<% if soll.present? %>
<%= soll %> h
<% else %>
<% end %>
</td>
<td>
<% if weekly.present? %>
<%= weekly %> h/Woche
<% else %>
<% end %>
</td>
<td>
<% actual = @actual_hours_per_week[[typ, art]] %>
<% if actual.present? %>
<%= actual %> h/Woche
<% else %>
<% end %>
</td>
<td>
<%= ende.present? ? ende.strftime("%d.%m.%Y") : "—" %>
</td>
<td>
<% percent = @completion_percent_by_typ_art[[typ, art]] %>
<%= percent.present? ? "#{percent} %" : "—" %>
</td>
</tr>
<% end %>
<tr
data-has-remaining="<%= has_remaining %>"
class="<%= 'd-none' unless has_remaining %>"
>
<td><%= typ.capitalize %></td>
<td><%= art %></td>
<td>
<%= remaining / 60 %> h <%= remaining % 60 %> min
</td>
<td>
<%= spent / 60 %> h <%= spent % 60 %> min
</td>
<td>
<%= number_with_precision(soll, precision: 2, strip_insignificant_zeros: true) %> h
</td>
<td>
<% if weekly.positive? %>
<%= number_with_precision(weekly, precision: 2, strip_insignificant_zeros: true) %> h/Woche
<% else %>
<% end %>
</td>
<td>
<% actual = @actual_hours_per_week[[typ, art]] %>
<% if actual.present? %>
<%= actual %> h/Woche
<% else %>
<% end %>
</td>
<td>
<%= ende.present? ? ende.strftime("%d.%m.%Y") : "—" %>
</td>
<td>
<% percent = @completion_percent_by_typ_art[[typ, art]] %>
<%= percent.nil? ? "—" : "#{percent} %" %>
</td>
</tr>
<% end %>
<% end %>
@ -250,6 +237,7 @@
<p><strong>Total: <%= @total_minutes / 60 %>h <%= @total_minutes % 60 %> min</strong></p>
<p>Propädeutikum: <%= @total_minutes_praktikum_prop / 60 %>h <%= @total_minutes_praktikum_prop % 60 %> min</p>
<p>Fachspezifikum: <%= @total_minutes_praktikum_fach / 60 %>h <%= @total_minutes_praktikum_fach % 60 %> min</p>
<p>Mediation: <%= @total_minutes_praktikum_mediation / 60 %>h <%= @total_minutes_praktikum_mediation % 60 %> min</p>
</div>
</div>
</div>
@ -404,12 +392,12 @@
<td>
<%= entry.praktikums_typ&.capitalize%>
<%= entry.praktikums_typ&.capitalize%>
</td>
<td>
<%= entry.entry_art&.capitalize %>
<%= entry.entry_art&.capitalize %>
</td>
@ -452,9 +440,9 @@
</div>
<div
id="entriesCount"
class="small text-muted mt-2"
aria-live="polite"
id="entriesCount"
class="small text-muted mt-2"
aria-live="polite"
>
Aktuell angezeigt: 0 · Gesamt: 0
</div>
@ -495,12 +483,12 @@
const entriesCount = document.getElementById("entriesCount");
if (
!tbody ||
!textFilter ||
!minYearFilter ||
!maxYearFilter ||
!columnToggleMenu ||
!entriesCount
!tbody ||
!textFilter ||
!minYearFilter ||
!maxYearFilter ||
!columnToggleMenu ||
!entriesCount
) return;
const headers = Array.from(table.querySelectorAll("thead th"));
@ -633,34 +621,34 @@
}).length;
entriesCount.textContent =
`Aktuell angezeigt: ${visibleRows} · Gesamt: ${rows.length}`;
`Aktuell angezeigt: ${visibleRows} · Gesamt: ${rows.length}`;
};
const applyFilters = () => {
const query = textFilter.value.trim().toLowerCase();
const minYear = minYearFilter.value
? Number(minYearFilter.value)
: null;
? Number(minYearFilter.value)
: null;
const maxYear = maxYearFilter.value
? Number(maxYearFilter.value)
: null;
? Number(maxYearFilter.value)
: null;
getRows().forEach(row => {
const rowYear = Number(row.dataset.year);
const matchesQuery =
!query || getVisibleRowText(row).includes(query);
!query || getVisibleRowText(row).includes(query);
const matchesMinYear =
minYear === null || rowYear >= minYear;
minYear === null || rowYear >= minYear;
const matchesMaxYear =
maxYear === null || rowYear <= maxYear;
maxYear === null || rowYear <= maxYear;
row.style.display =
matchesQuery && matchesMinYear && matchesMaxYear
? ""
: "none";
matchesQuery && matchesMinYear && matchesMaxYear
? ""
: "none";
});
updateEntriesCount();
@ -775,52 +763,19 @@
})();
</script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const table = document.getElementById("entriesTable");
const headers = table.querySelectorAll("th.sortable");
let sortDirection = 1;
headers.forEach((header, index) => {
header.style.cursor = 'pointer';
header.addEventListener("click", () => {
const tbody = table.querySelector("tbody");
const rows = Array.from(tbody.querySelectorAll("tr"));
const isNumeric = header.dataset.sort === 'km' || header.dataset.sort === 'pauschale';
const isDate = header.dataset.sort === 'date';
rows.sort((a, b) => {
const cellA = a.children[index].textContent.trim();
const cellB = b.children[index].textContent.trim();
if (isDate) {
return sortDirection * (new Date(cellA.split('.').reverse().join('-')) - new Date(cellB.split('.').reverse().join('-')));
}
if (isNumeric) {
const numA = parseFloat(cellA.replace(/[^\d,.-]/g, "").replace(",", "."));
const numB = parseFloat(cellB.replace(/[^\d,.-]/g, "").replace(",", "."));
return sortDirection * (numA - numB);
}
return sortDirection * cellA.localeCompare(cellB);
});
rows.forEach(row => tbody.appendChild(row));
sortDirection *= -1; // Toggle asc/desc
});
});
});
document.addEventListener('DOMContentLoaded', function () {
const timerElement = document.getElementById('live-timer');
if (!timerElement) return;
<% if @running_entry&.start_time.present? %>
const startedAt = new Date("<%= @running_entry.start_time.iso8601 %>");
const startedAt =
<% if @running_entry&.start_time.present? %>
new Date("<%= @running_entry.start_time.iso8601 %>");
<% else %>
null;
<% end %>
if (!startedAt) return;
const updateTimer = () => {
const now = new Date();
const diffMs = now - startedAt;
@ -842,9 +797,6 @@
const table = document.getElementById("combinationOverviewTable");
if (!checkbox || !table) return;
if (checkbox.dataset.initialized === "true") return;
checkbox.dataset.initialized = "true";
const updateVisibility = () => {
const showAll = checkbox.checked;
@ -862,7 +814,9 @@
});
};
checkbox.addEventListener("change", updateVisibility);
// Assignment statt addEventListener verhindert doppelte Handler
// und funktioniert auch nach einem Turbo-Cache/Restore sauber.
checkbox.onchange = updateVisibility;
updateVisibility();
};

Loading…
Cancel
Save