201 lines
6.9 KiB
HTML
201 lines
6.9 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Standards Informatiksicherheit{% endblock %}
|
|
{% block breadcrumb_items %}
|
|
<li class="breadcrumb-item active" aria-current="page">Standards</li>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1>Standards Informatiksicherheit</h1>
|
|
<div class="d-flex gap-2">
|
|
<span class="badge bg-primary">{{ dokumente|length }} Standards</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filter and Search Section -->
|
|
<div class="card mb-4">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label for="filter-search" class="form-label">Suchen</label>
|
|
<input type="text" class="form-control" id="filter-search" placeholder="Standard durchsuchen...">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="filter-status" class="form-label">Status</label>
|
|
<select class="form-select" id="filter-status">
|
|
<option value="">Alle</option>
|
|
<option value="active">Aktiv</option>
|
|
<option value="inactive">Inaktiv</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="filter-sort" class="form-label">Sortieren</label>
|
|
<select class="form-select" id="filter-sort">
|
|
<option value="nummer">Nummer</option>
|
|
<option value="name">Name</option>
|
|
<option value="gueltigkeit">Gültigkeit</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Standards Grid -->
|
|
<div class="row" id="standards-container">
|
|
{% for dokument in dokumente %}
|
|
<div class="col-lg-6 col-xl-4 mb-4 standard-item"
|
|
data-nummer="{{ dokument.nummer|lower }}"
|
|
data-name="{{ dokument.name|lower }}"
|
|
data-status="{% if dokument.aktiv %}active{% else %}inactive{% endif %}">
|
|
<div class="card standard-card h-100">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<span class="standard-number">{{ dokument.nummer }}</span>
|
|
{% if not dokument.aktiv %}
|
|
<span class="badge badge-status-inactive ms-2">Inaktiv</span>
|
|
{% else %}
|
|
<span class="badge badge-status-active ms-2">Aktiv</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="dropdown">
|
|
<button class="btn btn-sm btn-outline" type="button" data-bs-toggle="dropdown">
|
|
⋮
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="{% url 'standard_detail' nummer=dokument.nummer %}">Details anzeigen</a></li>
|
|
<li><a class="dropdown-item" href="{% url 'standard_json' dokument.nummer %}" download="{{ dokument.nummer }}.json">JSON herunterladen</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<h5 class="card-title">
|
|
<a href="{% url 'standard_detail' nummer=dokument.nummer %}" class="text-decoration-none">
|
|
{{ dokument.name }}
|
|
</a>
|
|
</h5>
|
|
|
|
<div class="standard-meta mb-3">
|
|
<div class="row g-2">
|
|
<div class="col-6">
|
|
<small class="text-muted">
|
|
<strong>Gültig von:</strong><br>
|
|
{{ dokument.gueltigkeit_von|default_if_none:"-" }}
|
|
</small>
|
|
</div>
|
|
<div class="col-6">
|
|
<small class="text-muted">
|
|
<strong>Gültig bis:</strong><br>
|
|
{{ dokument.gueltigkeit_bis|default_if_none:"Auf weiteres" }}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if dokument.autoren.all %}
|
|
<div class="mb-2">
|
|
<small class="text-muted">
|
|
<strong>Autoren:</strong>
|
|
{% for autor in dokument.autoren.all %}
|
|
{{ autor }}{% if not forloop.last %}, {% endif %}
|
|
{% endfor %}
|
|
</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if dokument.pruefende.all %}
|
|
<div class="mb-3">
|
|
<small class="text-muted">
|
|
<strong>Prüfende:</strong>
|
|
{% for pruefender in dokument.pruefende.all %}
|
|
{{ pruefender }}{% if not forloop.last %}, {% endif %}
|
|
{% endfor %}
|
|
</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<a href="{% url 'standard_detail' nummer=dokument.nummer %}" class="btn btn-primary btn-sm">
|
|
Details anzeigen
|
|
</a>
|
|
<div class="text-muted">
|
|
<small>
|
|
{% if dokument.history %}
|
|
Version vom {{ dokument.check_date|date:"d.m.Y" }}
|
|
{% endif %}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="col-12">
|
|
<div class="text-center py-5">
|
|
<h3 class="text-muted">Keine Standards gefunden</h3>
|
|
<p class="text-muted">Es wurden keine Standards gefunden, die Ihren Kriterien entsprechen.</p>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- JavaScript for filtering and sorting -->
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const searchInput = document.getElementById('filter-search');
|
|
const statusSelect = document.getElementById('filter-status');
|
|
const sortSelect = document.getElementById('filter-sort');
|
|
const container = document.getElementById('standards-container');
|
|
|
|
function filterAndSort() {
|
|
const searchTerm = searchInput.value.toLowerCase();
|
|
const statusFilter = statusSelect.value;
|
|
const sortBy = sortSelect.value;
|
|
|
|
let items = Array.from(container.querySelectorAll('.standard-item'));
|
|
|
|
// Filter
|
|
items = items.filter(item => {
|
|
const nummer = item.dataset.nummer;
|
|
const name = item.dataset.name;
|
|
const status = item.dataset.status;
|
|
|
|
const matchesSearch = !searchTerm ||
|
|
nummer.includes(searchTerm) ||
|
|
name.includes(searchTerm);
|
|
|
|
const matchesStatus = !statusFilter || status === statusFilter;
|
|
|
|
return matchesSearch && matchesStatus;
|
|
});
|
|
|
|
// Sort
|
|
items.sort((a, b) => {
|
|
switch(sortBy) {
|
|
case 'nummer':
|
|
return a.dataset.nummer.localeCompare(b.dataset.nummer);
|
|
case 'name':
|
|
return a.dataset.name.localeCompare(b.dataset.name);
|
|
case 'gueltigkeit':
|
|
// This would need additional data attributes for proper sorting
|
|
return a.dataset.nummer.localeCompare(b.dataset.nummer);
|
|
default:
|
|
return 0;
|
|
}
|
|
});
|
|
|
|
// Reorder DOM
|
|
items.forEach(item => container.appendChild(item));
|
|
|
|
// Show/hide no results message
|
|
const noResults = container.querySelector('.col-12 .text-center');
|
|
if (noResults) {
|
|
noResults.parentElement.style.display = items.length === 0 ? 'block' : 'none';
|
|
}
|
|
}
|
|
|
|
searchInput.addEventListener('input', filterAndSort);
|
|
statusSelect.addEventListener('change', filterAndSort);
|
|
sortSelect.addEventListener('change', filterAndSort);
|
|
});
|
|
</script>
|
|
{% endblock %} |