Files
stiftung-management-system/app/templates/stiftung/land_list.html
Jan Remmer Siebels 544284dd8b Remove obsolete dashboard functionality
- Remove dashboard view from urls.py and views.py
- Delete dashboard.html template
- Remove dashboard navigation link from base.html
- Replace all dashboard redirects with home redirects in views.py
- Update all breadcrumb links from 'Dashboard' to 'Home' in templates
- Update German text from 'Dashboard' to 'Startseite' in auth templates
- Update 'Zurück zum Dashboard' links to 'Zurück zur Startseite'

The dashboard was redundant with the home page functionality.
All navigation now directs users to the main home page instead.
System check passes without issues after removal.
2025-10-05 20:49:48 +02:00

543 lines
29 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}Ländereien - Stiftungsverwaltung{% endblock %}
{% block content %}
<!-- Header -->
<div class="row mb-4">
<div class="col-md-6">
<h1 class="h3">
<i class="fas fa-map text-success me-2"></i>
Ländereien verwalten
</h1>
</div>
<div class="col-md-6 text-end">
<a href="{% url 'stiftung:land_create' %}" class="btn btn-success">
<i class="fas fa-plus me-2"></i>Neue Länderei
</a>
</div>
</div>
<!-- Search and Filters -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-search me-2"></i>Suche & Filter
</h6>
</div>
<div class="card-body">
<form method="get" class="row g-3">
<div class="col-md-4">
<label for="search" class="form-label">Suche</label>
<input type="text" class="form-control" id="search" name="search"
value="{{ search_query }}" placeholder="Lfd. Nr., Gemeinde, Gemarkung...">
</div>
<div class="col-md-3">
<label for="gemeinde" class="form-label">Gemeinde</label>
<select class="form-control" id="gemeinde" name="gemeinde">
<option value="">Alle Gemeinden</option>
{% for gemeinde in gemeinden %}
<option value="{{ gemeinde }}" {% if gemeinde == gemeinde_filter %}selected{% endif %}>
{{ gemeinde }}
</option>
{% endfor %}
</select>
</div>
<div class="col-md-3">
<label for="aktiv" class="form-label">Status</label>
<select class="form-control" id="aktiv" name="aktiv">
<option value="">Alle</option>
<option value="true" {% if aktiv_filter == 'true' %}selected{% endif %}>Aktiv</option>
<option value="false" {% if aktiv_filter == 'false' %}selected{% endif %}>Inaktiv</option>
</select>
</div>
<div class="col-md-2">
<label class="form-label">&nbsp;</label>
<div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-search me-2"></i>Suchen
</button>
</div>
</div>
</form>
</div>
</div>
<!-- Charts Row -->
<div class="row mb-4">
<!-- Usage Chart -->
<div class="col-lg-4 mb-3">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-percentage me-2"></i>Flächennutzung
</h6>
</div>
<div class="card-body">
<div class="row text-center mb-3">
<div class="col-4">
<div class="h6 mb-0 text-success">{{ stats.pct_wald|default:0 }}%</div>
<div class="small text-muted">Wald</div>
</div>
<div class="col-4">
<div class="h6 mb-0 text-info">{{ stats.pct_acker|default:0 }}%</div>
<div class="small text-muted">Acker</div>
</div>
<div class="col-4">
<div class="h6 mb-0 text-warning">{{ stats.pct_gruenland|default:0 }}%</div>
<div class="small text-muted">Grünland</div>
</div>
</div>
<div style="height: 200px;">
<canvas id="usageChart"></canvas>
</div>
</div>
</div>
</div>
<!-- Sizes Chart -->
<div class="col-lg-4 mb-3">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-chart-bar me-2"></i>Größen (Top 30)
</h6>
</div>
<div class="card-body">
<div class="text-center mb-3">
<div class="h6 mb-0">{{ stats.total_plots }}</div>
<div class="small text-muted">Grundstücke gesamt</div>
</div>
<div style="height: 200px;">
<canvas id="sizesChart"></canvas>
</div>
</div>
</div>
</div>
<!-- Verpachtung Chart -->
<div class="col-lg-4 mb-3">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-handshake me-2"></i>Verpachtungsstatus
</h6>
</div>
<div class="card-body">
<div class="row text-center mb-3">
<div class="col-6">
<div class="h6 mb-0 text-success">{{ stats.pct_verpachtet|default:0 }}%</div>
<div class="small text-muted">Verpachtet</div>
</div>
<div class="col-6">
<div class="h6 mb-0 text-secondary">{{ stats.pct_unveerpachtet|default:0 }}%</div>
<div class="small text-muted">Verfügbar</div>
</div>
</div>
<div style="height: 200px;">
<canvas id="verpachtungChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Results -->
<div class="card shadow">
<div class="card-header py-3 d-flex justify-content-between align-items-center">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-list me-2"></i>Ergebnisse
{% if page_obj %}
<span class="badge bg-secondary ms-2">{{ page_obj.paginator.count }} Ländereien</span>
{% endif %}
</h6>
<div>
<a href="{% url 'stiftung:home' %}" class="btn btn-outline-secondary btn-sm">
<i class="fas fa-arrow-left me-2"></i>Zurück zur Startseite
</a>
</div>
</div>
<div class="card-body">
{% if page_obj %}
<div class="table-responsive">
<table class="table table-hover">
<thead class="table-light">
<tr>
<th class="text-nowrap">
<a class="text-decoration-none" href="?sort=lfd_nr&dir={% if sort == 'lfd_nr' and dir != 'desc' %}desc{% else %}asc{% endif %}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}">Lfd. Nr.</a>
</th>
<th class="text-nowrap">
<a class="text-decoration-none" href="?sort=gemeinde&dir={% if sort == 'gemeinde' and dir != 'desc' %}desc{% else %}asc{% endif %}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}">Gemeinde</a>
</th>
<th class="text-nowrap">
<a class="text-decoration-none" href="?sort=gemarkung&dir={% if sort == 'gemarkung' and dir != 'desc' %}desc{% else %}asc{% endif %}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}">Gemarkung</a>
</th>
<th class="text-nowrap">
<a class="text-decoration-none" href="?sort=flur&dir={% if sort == 'flur' and dir != 'desc' %}desc{% else %}asc{% endif %}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}">Flur</a>/<a class="text-decoration-none" href="?sort=flurstueck&dir={% if sort == 'flurstueck' and dir != 'desc' %}desc{% else %}asc{% endif %}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}">Flurstück</a>
</th>
<th class="text-nowrap">
<a class="text-decoration-none" href="?sort=groesse&dir={% if sort == 'groesse' and dir != 'desc' %}desc{% else %}asc{% endif %}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}">Größe</a>
</th>
<th class="text-nowrap">
<a class="text-decoration-none" href="?sort=verp&dir={% if sort == 'verp' and dir != 'desc' %}desc{% else %}asc{% endif %}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}">Verpachtet</a>
</th>
<th class="text-nowrap">
<a class="text-decoration-none" href="?sort=grad&dir={% if sort == 'grad' and dir != 'desc' %}desc{% else %}asc{% endif %}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}">Verpachtungsgrad</a>
</th>
<th>Status</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for land in page_obj %}
<tr>
<td>
<strong>{{ land.lfd_nr }}</strong>
{% if land.ew_nummer %}
<br><small class="text-muted">{{ land.ew_nummer }}</small>
{% endif %}
</td>
<td>
<strong>{{ land.gemeinde }}</strong>
<br><small class="text-muted">{{ land.amtsgericht }}</small>
</td>
<td>{{ land.gemarkung }}</td>
<td>
Flur {{ land.flur }}<br>
Flurstück {{ land.flurstueck }}
</td>
<td>
<strong>{{ land.groesse_qm|floatformat:0 }} qm</strong>
<small class="text-muted">({{ land.groesse_hektar|floatformat:2 }} ha)</small>
<br>
<small class="text-muted">
{% if land.gruenland_qm > 0 %}G: {{ land.gruenland_qm|floatformat:0 }}{% endif %}
{% if land.acker_qm > 0 %} A: {{ land.acker_qm|floatformat:0 }}{% endif %}
{% if land.wald_qm > 0 %} W: {{ land.wald_qm|floatformat:0 }}{% endif %}
{% if land.sonstiges_qm > 0 %} S: {{ land.sonstiges_qm|floatformat:0 }}{% endif %}
</small>
</td>
<td>
{{ land.get_verpachtete_flaeche_aktuell|floatformat:0 }} qm
{% if land.flaeche_alte_liste %}
<br><small class="text-muted">Alt: {{ land.flaeche_alte_liste|floatformat:0 }} qm</small>
{% endif %}
</td>
<td>
{% with verpachtungsgrad=land.get_verpachtungsgrad %}
{% if verpachtungsgrad > 90 %}
<span class="badge bg-success">{{ verpachtungsgrad|floatformat:1 }}%</span>
{% elif verpachtungsgrad > 70 %}
<span class="badge bg-warning">{{ verpachtungsgrad|floatformat:1 }}%</span>
{% else %}
<span class="badge bg-danger">{{ verpachtungsgrad|floatformat:1 }}%</span>
{% endif %}
{% endwith %}
</td>
<td>
{% if land.aktiv %}
<span class="badge bg-success">Aktiv</span>
{% else %}
<span class="badge bg-secondary">Inaktiv</span>
{% endif %}
</td>
<td>
<div class="btn-group" role="group">
<a href="{% url 'stiftung:land_detail' land.pk %}"
class="btn btn-sm btn-outline-primary" title="Anzeigen">
<i class="fas fa-eye"></i>
</a>
<a href="{% url 'stiftung:land_update' land.pk %}"
class="btn btn-sm btn-outline-warning" title="Bearbeiten">
<i class="fas fa-edit"></i>
</a>
<a href="{% url 'stiftung:land_delete' land.pk %}"
class="btn btn-sm btn-outline-danger" title="Löschen">
<i class="fas fa-trash"></i>
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if page_obj.has_other_pages %}
<nav aria-label="Ländereien Navigation">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}{% if sort %}&sort={{ sort }}&dir={{ dir }}{% endif %}">
<i class="fas fa-angle-double-left"></i>
</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}{% if sort %}&sort={{ sort }}&dir={{ dir }}{% endif %}">
<i class="fas fa-angle-left"></i>
</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}{% if sort %}&sort={{ sort }}&dir={{ dir }}{% endif %}">
{{ num }}
</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}{% if sort %}&sort={{ sort }}&dir={{ dir }}{% endif %}">
<i class="fas fa-angle-right"></i>
</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if search_query %}&search={{ search_query }}{% endif %}{% if gemeinde_filter %}&gemeinde={{ gemeinde_filter }}{% endif %}{% if aktiv_filter %}&aktiv={{ aktiv_filter }}{% endif %}{% if sort %}&sort={{ sort }}&dir={{ dir }}{% endif %}">
<i class="fas fa-angle-double-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="text-center py-5">
<i class="fas fa-map fa-3x text-muted mb-3"></i>
<h5 class="text-muted">Keine Ländereien gefunden</h5>
<p class="text-muted">
{% if search_query or gemeinde_filter or aktiv_filter %}
Versuchen Sie andere Suchkriterien oder
<a href="{% url 'stiftung:land_list' %}">alle Ländereien anzeigen</a>.
{% else %}
Erstellen Sie Ihre erste Länderei mit dem Button oben.
{% endif %}
</p>
</div>
{% endif %}
</div>
</div>
{% endblock %}
{% block javascript %}
<script>
// Auto-submit form when filters change
document.getElementById('gemeinde').addEventListener('change', function() {
this.form.submit();
});
document.getElementById('aktiv').addEventListener('change', function() {
this.form.submit();
});
</script>
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
try {
console.log('Initializing charts...');
// Doughnut chart for usage
const uctx = document.getElementById('usageChart');
if (uctx) {
console.log('Found usageChart canvas');
// Simple data without filtering first to test
const waldVal = parseFloat('{{ stats.sum_wald_qm|default:0|floatformat:2 }}') || 0;
const ackerVal = parseFloat('{{ stats.sum_acker_qm|default:0|floatformat:2 }}') || 0;
const gruenlandVal = parseFloat('{{ stats.sum_gruenland_qm|default:0|floatformat:2 }}') || 0;
const sonstigesVal = parseFloat('{{ stats.sum_sonstiges_qm|default:0|floatformat:2 }}') || 0;
console.log('Chart data:', {wald: waldVal, acker: ackerVal, gruenland: gruenlandVal, sonstiges: sonstigesVal});
// Only include categories with data > 0
const chartData = [];
const chartLabels = [];
const chartColors = [];
const chartBorders = [];
if (waldVal > 0) {
chartData.push(waldVal);
chartLabels.push('Wald');
chartColors.push('rgba(0, 66, 37, 0.8)');
chartBorders.push('#004225');
}
if (ackerVal > 0) {
chartData.push(ackerVal);
chartLabels.push('Acker');
chartColors.push('rgba(0, 104, 55, 0.8)');
chartBorders.push('#006837');
}
if (gruenlandVal > 0) {
chartData.push(gruenlandVal);
chartLabels.push('Grünland');
chartColors.push('rgba(253, 126, 20, 0.8)');
chartBorders.push('#fd7e14');
}
if (sonstigesVal > 0) {
chartData.push(sonstigesVal);
chartLabels.push('Sonstiges');
chartColors.push('rgba(108, 117, 125, 0.8)');
chartBorders.push('#6c757d');
}
if (chartData.length > 0) {
new Chart(uctx, {
type: 'doughnut',
data: {
labels: chartLabels,
datasets: [{
data: chartData,
backgroundColor: chartColors,
borderColor: chartBorders,
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'bottom',
labels: { padding: 10, fontSize: 12 }
},
tooltip: {
callbacks: {
label: function(context) {
return context.label + ': ' + context.raw.toLocaleString() + ' qm';
}
}
}
}
}
});
console.log('Usage chart created successfully');
} else {
console.log('No data for usage chart');
}
} else {
console.log('usageChart canvas not found');
}
// Bar chart for sizes
const ctx = document.getElementById('sizesChart');
if (ctx) {
console.log('Found sizesChart canvas');
const labels = JSON.parse('{{ size_chart_labels_json|escapejs }}');
const dataVals = JSON.parse('{{ size_chart_values_json|escapejs }}');
console.log('Bar chart data:', {labels: labels.length, values: dataVals.length});
new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Größe (qm)',
data: dataVals,
backgroundColor: 'rgba(0, 104, 55, 0.6)',
borderColor: '#006837',
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: { ticks: { autoSkip: true, maxTicksLimit: 8 } },
y: { beginAtZero: true }
},
plugins: {
legend: { display: false },
tooltip: { callbacks: { label: function(ctx) { return ctx.parsed.y.toLocaleString() + ' qm'; } } }
}
}
});
console.log('Bar chart created successfully');
} else {
console.log('sizesChart canvas not found');
}
// Doughnut chart for verpachtung
const vctx = document.getElementById('verpachtungChart');
if (vctx) {
console.log('Found verpachtungChart canvas');
const verpachtet = {{ stats.verpachtete_plots|default:0 }};
const verfuegbar = {{ stats.unveerpachtete_plots|default:0 }};
console.log('Verpachtung data:', {verpachtet: verpachtet, verfuegbar: verfuegbar});
if (verpachtet > 0 || verfuegbar > 0) {
const verpachtungData = [];
const verpachtungLabels = [];
const verpachtungColors = [];
if (verpachtet > 0) {
verpachtungData.push(verpachtet);
verpachtungLabels.push('Verpachtet');
verpachtungColors.push('rgba(40, 167, 69, 0.8)');
}
if (verfuegbar > 0) {
verpachtungData.push(verfuegbar);
verpachtungLabels.push('Verfügbar');
verpachtungColors.push('rgba(108, 117, 125, 0.8)');
}
new Chart(vctx, {
type: 'doughnut',
data: {
labels: verpachtungLabels,
datasets: [{
data: verpachtungData,
backgroundColor: verpachtungColors,
borderWidth: 2,
borderColor: '#fff'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'bottom',
labels: { padding: 10, fontSize: 12 }
},
tooltip: {
callbacks: {
label: function(ctx) {
const percentage = ((ctx.parsed / (verpachtet + verfuegbar)) * 100).toFixed(1);
return ctx.label + ': ' + ctx.parsed + ' (' + percentage + '%)';
}
}
}
}
}
});
console.log('Verpachtung chart created successfully');
} else {
console.log('No data for verpachtung chart');
}
} else {
console.log('verpachtungChart canvas not found');
}
} catch (e) {
console.error('Chart initialization error:', e);
}
});
</script>
{% endblock %}