let the year be changeable

This commit is contained in:
NABU Jena 2025-07-09 08:29:06 +02:00
parent 1368217187
commit e047dade4d
2 changed files with 121 additions and 12 deletions

View file

@ -281,6 +281,27 @@
height: 20px; height: 20px;
border-radius: 4px; border-radius: 4px;
} }
/* Jahr-Navigation */
.year-navigation .btn {
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.year-navigation .btn:hover:not(:disabled) {
background-color: rgba(255, 255, 255, 0.2);
transform: scale(1.1);
}
.year-navigation .btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
</style> </style>
{% endblock %} {% endblock %}
@ -295,11 +316,40 @@
</div> </div>
</div> </div>
<!-- 1. Übersicht aktuelles Jahr --> <!-- 1. Übersicht ausgewähltes Jahr mit Navigation -->
<div class="section-header"> <div class="section-header d-flex justify-content-between align-items-center">
<h2 class="mb-0"> <h2 class="mb-0">
<i class="fas fa-calendar-alt"></i> Übersicht {{ current_year }} <i class="fas fa-calendar-alt"></i> Übersicht {{ selected_year }}
</h2> </h2>
<div class="year-navigation d-flex align-items-center">
{% if can_go_previous %}
<a href="?year={{ previous_year }}" class="btn btn-outline-light me-2" title="Vorheriges Jahr ({{ previous_year }})">
<i class="fas fa-chevron-left"></i>
</a>
{% else %}
<button class="btn btn-outline-light me-2" disabled title="Keine Daten vor {{ earliest_year }}">
<i class="fas fa-chevron-left"></i>
</button>
{% endif %}
<span class="mx-3 fw-bold">{{ selected_year }}</span>
{% if can_go_next %}
<a href="?year={{ next_year }}" class="btn btn-outline-light ms-2" title="Nächstes Jahr ({{ next_year }})">
<i class="fas fa-chevron-right"></i>
</a>
{% else %}
<button class="btn btn-outline-light ms-2" disabled title="Aktuelles Jahr erreicht">
<i class="fas fa-chevron-right"></i>
</button>
{% endif %}
{% if selected_year != current_year %}
<a href="?year={{ current_year }}" class="btn btn-light ms-3" title="Zurück zu {{ current_year }}">
<i class="fas fa-calendar-day me-1"></i>{{ current_year }}
</a>
{% endif %}
</div>
</div> </div>
<div class="row g-4 mb-4"> <div class="row g-4 mb-4">
@ -441,7 +491,7 @@
<div class="col-md-6"> <div class="col-md-6">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h5 class="mb-0">{{ current_year }}</h5> <h5 class="mb-0">{{ selected_year }}</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
{% if circumstances_this_year %} {% if circumstances_this_year %}
@ -456,7 +506,7 @@
</div> </div>
{% else %} {% else %}
<div class="text-center text-muted"> <div class="text-center text-muted">
<p>Keine Daten für {{ current_year }} verfügbar</p> <p>Keine Daten für {{ selected_year }} verfügbar</p>
</div> </div>
{% endif %} {% endif %}
</div> </div>
@ -516,6 +566,37 @@ document.addEventListener('DOMContentLoaded', function() {
} }
} }
}); });
// Tastatur-Navigation für Jahre
document.addEventListener('keydown', function(e) {
// Nur reagieren wenn kein Input-Element fokussiert ist
if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') {
return;
}
if (e.key === 'ArrowLeft') {
// Vorheriges Jahr
const prevButton = document.querySelector('a[href*="year={{ previous_year }}"]');
if (prevButton) {
window.location.href = prevButton.href;
}
} else if (e.key === 'ArrowRight') {
// Nächstes Jahr
const nextButton = document.querySelector('a[href*="year={{ next_year }}"]');
if (nextButton) {
window.location.href = nextButton.href;
}
} else if (e.key === 'Home') {
// Zurück zum aktuellen Jahr
const currentYearButton = document.querySelector('a[href*="year={{ current_year }}"]');
if (currentYearButton) {
window.location.href = currentYearButton.href;
} else {
// Falls schon im aktuellen Jahr, zur Statistik-Hauptseite
window.location.href = window.location.pathname;
}
}
});
}); });
</script> </script>
{% endblock %} {% endblock %}

View file

@ -1,6 +1,6 @@
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Count, Q from django.db.models import Count, Q, Min
from django.utils import timezone from django.utils import timezone
from datetime import datetime from datetime import datetime
from bird.models import FallenBird, Bird, BirdStatus, Circumstance from bird.models import FallenBird, Bird, BirdStatus, Circumstance
@ -13,9 +13,31 @@ class StatisticView(LoginRequiredMixin, TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
# Aktuelles Jahr # Jahr aus URL-Parameter oder aktuelles Jahr
selected_year = self.request.GET.get('year', timezone.now().year)
try:
selected_year = int(selected_year)
except (ValueError, TypeError):
selected_year = timezone.now().year
current_year = timezone.now().year current_year = timezone.now().year
# Stelle sicher, dass das ausgewählte Jahr nicht in der Zukunft liegt
if selected_year > current_year:
selected_year = current_year
# Finde das früheste Jahr mit Daten
earliest_year_with_data = FallenBird.objects.aggregate(
earliest=Min('date_found__year')
)['earliest']
if earliest_year_with_data is None:
earliest_year_with_data = current_year
# Stelle sicher, dass das ausgewählte Jahr nicht vor dem frühesten Jahr liegt
if selected_year < earliest_year_with_data:
selected_year = earliest_year_with_data
# Lade aktive Konfiguration # Lade aktive Konfiguration
try: try:
config = StatisticConfiguration.objects.get(is_active=True) config = StatisticConfiguration.objects.get(is_active=True)
@ -31,11 +53,17 @@ class StatisticView(LoginRequiredMixin, TemplateView):
context['config'] = config context['config'] = config
context['current_year'] = current_year context['current_year'] = current_year
context['selected_year'] = selected_year
context['earliest_year'] = earliest_year_with_data
context['can_go_previous'] = selected_year > earliest_year_with_data
context['can_go_next'] = selected_year < current_year
context['previous_year'] = selected_year - 1 if context['can_go_previous'] else None
context['next_year'] = selected_year + 1 if context['can_go_next'] else None
# 1. Jahresstatistik # 1. Jahresstatistik
if config.show_year_total_patients: if config.show_year_total_patients:
patients_this_year = FallenBird.objects.filter( patients_this_year = FallenBird.objects.filter(
date_found__year=current_year date_found__year=selected_year
).count() ).count()
context['patients_this_year'] = patients_this_year context['patients_this_year'] = patients_this_year
@ -46,7 +74,7 @@ class StatisticView(LoginRequiredMixin, TemplateView):
for group in year_groups: for group in year_groups:
status_ids = list(group.status_list.values_list('id', flat=True)) status_ids = list(group.status_list.values_list('id', flat=True))
year_count = FallenBird.objects.filter( year_count = FallenBird.objects.filter(
date_found__year=current_year, date_found__year=selected_year,
status__id__in=status_ids status__id__in=status_ids
).count() ).count()
@ -144,10 +172,10 @@ class StatisticView(LoginRequiredMixin, TemplateView):
context['bird_stats'] = bird_stats context['bird_stats'] = bird_stats
# 4. Fundumstände-Statistiken (unverändert) # 4. Fundumstände-Statistiken
# Fundumstände für aktuelles Jahr # Fundumstände für ausgewähltes Jahr
circumstances_this_year = FallenBird.objects.filter( circumstances_this_year = FallenBird.objects.filter(
date_found__year=current_year, date_found__year=selected_year,
find_circumstances__isnull=False find_circumstances__isnull=False
).values('find_circumstances__name', 'find_circumstances__description').annotate( ).values('find_circumstances__name', 'find_circumstances__description').annotate(
count=Count('id') count=Count('id')