Design suggestion from AI. Not very useable

This commit is contained in:
2025-11-05 10:16:56 +01:00
parent 0cd09d0878
commit 32917113f2
14 changed files with 3257 additions and 269 deletions

View File

@@ -1,51 +1,190 @@
{% extends "base.html" %}
{% load mptt_tags %}
{% block title %}Referenz: {{ referenz.Path }}{% endblock %}
{% block breadcrumb_items %}
<li class="breadcrumb-item"><a href="/referenzen/">Referenzen</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ referenz.Path }}</li>
{% endblock %}
{% block content %}
<h1><a href="../{{ referenz.ParentID }}"></a>{{ referenz.Path }}</h1>
{% if referenz.erklaerung %}
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center bg-secondary text-light">
<h3 class="h5 m-0">Beschreibung</h3>
{% if referenz.url %}
<span class="badge bg-light text-black">
<a href="{{ referenz.url }}">Link</a>
</span>{% endif %}
</div>
<div class="card-body p-2">
<div class="row">
<div class="col-lg-8">
<!-- Referenz Header -->
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<div class="d-flex justify-content-between align-items-start">
<div>
<h1 class="h3 mb-2">🔗 {{ referenz.Path }}</h1>
{% if referenz.ParentID %}
<small class="opacity-75">
<a href="/referenzen/{{ referenz.ParentID }}/" class="text-white">
← Zurück zu übergeordneter Referenz
</a>
</small>
{% endif %}
</div>
{% if referenz.url %}
<a href="{{ referenz.url }}" class="btn btn-light btn-sm" target="_blank">
🔗 Externer Link
</a>
{% endif %}
</div>
</div>
{% if referenz.erklaerung %}
<div class="card-body">
<h5 class="card-title">📖 Beschreibung</h5>
{% for typ, html in referenz.erklaerung %}
{% if html %}<div>{{ html|safe }}</div>{% endif %}{% endfor %}
</div>
</div>
{% endif %}
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center bg-secondary text-light">
<h3 class="h5 m-0">Referenzierte Vorgaben</h3>
{% if html %}
<div class="content-section">{{ html|safe }}</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
<div class="card-body p-2">
{% recursetree referenz.children %}
{% if not node == referenz %}
{#<a href="../{{node.id}}">#}
{{ node.Path }}
{#</a>#}
{% else %}
{{ node.Path }}
{% endif %}
<br>
{% if node.referenziertvon %}
<ul>
<!-- Referenzierte Vorgaben -->
<div class="card">
<div class="card-header">
<h3 class="h5 mb-0">📝 Referenzierte Vorgaben</h3>
</div>
<div class="card-body">
{% recursetree referenz.children %}
{% if not node == referenz %}
<div class="mb-3 p-3 border rounded">
<h6 class="mb-2">{{ node.Path }}</h6>
{% if node.referenziertvon %}
<div class="ms-3">
<small class="text-muted">Referenziert von:</small>
<ul class="list-unstyled mb-0">
{% for ref in node.referenziertvon %}
<li class="mb-1">
<a href="/dokumente/{{ ref.dokument.nummer }}/#{{ref.Vorgabennummer}}"
class="text-decoration-none">
<span class="badge bg-secondary me-2">{{ ref.Vorgabennummer }}</span>
{{ ref.titel }}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
{% endif %}
{% if node.referenziertvon %}
<div class="ms-3 mb-3">
<small class="text-muted">Referenziert von:</small>
<ul class="list-unstyled mb-0">
{% for ref in node.referenziertvon %}
<li><a href="{% url 'standard_detail' nummer=ref.dokument.nummer %}#{{ref.Vorgabennummer}}">{{ref}}</a></li>
<li class="mb-1">
<a href="/dokumente/{{ ref.dokument.nummer }}/#{{ref.Vorgabennummer}}"
class="text-decoration-none">
<span class="badge bg-secondary me-2">{{ ref.Vorgabennummer }}</span>
{{ ref.titel }}
</a>
</li>
{% endfor %}
</ul>
<br>
{% endif %}
{% if not node.is_leaf_node %}
{{ children }}
{% endif %}
{% endrecursetree %}
</ul>
</div>
{% endif %}
{% if not node.is_leaf_node %}
<div class="ms-3">
{{ children }}
</div>
{% endif %}
{% endrecursetree %}
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Quick Actions -->
<div class="card mb-4 sticky-top" style="top: 1rem;">
<div class="card-header">
<h5 class="mb-0">⚡ Aktionen</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
{% if referenz.ParentID %}
<a href="/referenzen/{{ referenz.ParentID }}/" class="btn btn-outline btn-sm">
← Zurück
</a>
{% endif %}
<a href="/referenzen/" class="btn btn-outline btn-sm">
🌳 Zur Baumansicht
</a>
{% if referenz.url %}
<a href="{{ referenz.url }}" class="btn btn-outline btn-sm" target="_blank">
🔗 Externer Link
</a>
{% endif %}
</div>
</div>
</div>
<!-- Statistics -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">📊 Informationen</h5>
</div>
<div class="card-body">
<div class="mb-3">
<small class="text-muted">Referenz-ID</small>
<p class="mb-0 fw-bold">{{ referenz.id }}</p>
</div>
{% if referenz.ParentID %}
<div class="mb-3">
<small class="text-muted">Übergeordnete Referenz</small>
<p class="mb-0">
<a href="/referenzen/{{ referenz.ParentID }}/" class="text-decoration-none">
ID: {{ referenz.ParentID }}
</a>
</p>
</div>
{% endif %}
<div class="mb-3">
<small class="text-muted">Anzahl Unterelemente</small>
<p class="mb-0 fw-bold">{{ referenz.get_children.count }}</p>
</div>
</div>
</div>
<!-- Navigation Help -->
<div class="card">
<div class="card-header">
<h5 class="mb-0">💡 Hinweise</h5>
</div>
<div class="card-body">
<ul class="small mb-0">
<li>Diese Seite zeigt Details zur ausgewählten Referenz</li>
<li>Verknüpfte Vorgaben sind direkt verlinkt</li>
<li>Nutzen Sie die Baumansicht für die Übersicht</li>
</ul>
</div>
</div>
</div>
</div>
<style>
.content-section {
line-height: 1.6;
margin-bottom: 1rem;
}
.content-section h1, .content-section h2, .content-section h3 {
margin-top: 1.5rem;
margin-bottom: 1rem;
}
.border {
border-color: var(--border-color) !important;
}
.badge {
font-size: 0.75rem;
}
</style>
{% endblock %}

View File

@@ -1,21 +1,466 @@
{% extends "base.html" %}
{% block title %}Referenzen{% endblock %}
{% block breadcrumb_items %}
<li class="breadcrumb-item active" aria-current="page">Referenzen</li>
{% endblock %}
{% block content %}
<h1>Referenzen</h1>
<div class="row">
<div class="col-lg-8">
<!-- Header -->
<div class="card mb-4">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<h1 class="h3 mb-0">🔗 Referenzbaum</h1>
<div class="d-flex gap-2">
<button class="btn btn-outline btn-sm" onclick="expandAll()">
Alle ausklappen
</button>
<button class="btn btn-outline btn-sm" onclick="collapseAll()">
Alle einklappen
</button>
</div>
</div>
</div>
<div class="card-body">
<p class="text-muted mb-0">
Hier finden Sie alle Referenzen und Querverbindungen zwischen den Standards und Vorgaben.
Klicken Sie auf die Pfeile um den Baum zu navigieren.
</p>
</div>
</div>
<div>
{% load mptt_tags %}
<ul class="tree">
{% recursetree referenzen %}
<li>
<a href="{{node.id}}">{{ node.name_nummer }}{% if node.name_text %} ({{node.name_text}}){% endif %}</a>
{% if not node.is_leaf_node %}
<ul class="children">
<!-- Search and Filter -->
<div class="card mb-4">
<div class="card-body">
<div class="row g-3">
<div class="col-md-8">
<label for="tree-search" class="form-label">🔍 Referenzen durchsuchen</label>
<input type="text"
class="form-control"
id="tree-search"
placeholder="Suchbegriff eingeben..."
onkeyup="filterTree()">
</div>
<div class="col-md-4">
<label for="tree-filter" class="form-label">🏷️ Filter</label>
<select class="form-select" id="tree-filter" onchange="filterTree()">
<option value="">Alle anzeigen</option>
<option value="has-children">Mit Unterelementen</option>
<option value="leaf-only">Nur Endpunkte</option>
</select>
</div>
</div>
</div>
</div>
<!-- Interactive Tree -->
<div class="card">
<div class="card-body">
{% load mptt_tags %}
<div id="tree-container">
<ul class="tree-root">
{% recursetree referenzen %}
<li class="tree-node" data-node-id="{{ node.id }}" data-node-text="{{ node.name_text|default:'' }} {{ node.name_nummer|default:'' }}">
<div class="tree-node-content" onclick="toggleNode(this)">
{% if not node.is_leaf_node %}
<span class="tree-toggle"></span>
{% else %}
<span class="tree-toggle-placeholder"></span>
{% endif %}
<a href="{{ node.id }}" class="tree-link">
{% if node.name_nummer %}
<span class="tree-number">{{ node.name_nummer }}</span>
{% endif %}
{% if node.name_text %}
<span class="tree-text">{{ node.name_text }}</span>
{% endif %}
</a>
<div class="tree-node-meta">
{% if not node.is_leaf_node %}
<span class="badge bg-info">{{ node.get_children.count }} Unterelemente</span>
{% else %}
<span class="badge bg-secondary">Endpunkt</span>
{% endif %}
</div>
</div>
{% if not node.is_leaf_node %}
<ul class="tree-children">
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
</div>
{% if not referenzen %}
<div class="text-center py-5">
<span style="font-size: 3rem;">🔗</span>
<h4 class="text-muted mt-3">Keine Referenzen gefunden</h4>
<p class="text-muted">Es wurden keine Referenzen in der Datenbank gefunden.</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Statistics -->
<div class="card mb-4 sticky-top" style="top: 1rem;">
<div class="card-header">
<h5 class="mb-0">📊 Statistiken</h5>
</div>
<div class="card-body">
<div class="row text-center mb-3">
<div class="col-6">
<h4 class="text-primary mb-1">{{ referenzen|length }}</h4>
<small class="text-muted">Gesamt</small>
</div>
<div class="col-6">
<h4 class="text-success mb-1">
{% for node in referenzen %}
{% if node.is_leaf_node %}
{% if forloop.first %}1{% else %}{{ forloop.counter }}{% endif %}
{% endif %}
{% endfor %}
</h4>
<small class="text-muted">Endpunkte</small>
</div>
</div>
<div class="border-top pt-3">
<h6 class="text-muted mb-2">Baumtiefe</h6>
<div class="progress" style="height: 8px;">
<div class="progress-bar bg-primary" style="width: 75%"></div>
</div>
<small class="text-muted">Maximale Tiefe: 4 Ebenen</small>
</div>
</div>
</div>
<!-- Quick Navigation -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">🧭 Navigation</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<button class="btn btn-outline btn-sm" onclick="scrollToTop()">
⬆️ Zum Anfang
</button>
<button class="btn btn-outline btn-sm" onclick="findNextMatch()">
⬇️ Nächster Treffer
</button>
<button class="btn btn-outline btn-sm" onclick="resetFilters()">
🔄 Filter zurücksetzen
</button>
</div>
</div>
</div>
<!-- Help -->
<div class="card">
<div class="card-header">
<h5 class="mb-0">💡 Hinweise</h5>
</div>
<div class="card-body">
<ul class="small mb-0">
<li>Klicken Sie auf ▼/▶ um Knoten ein-/auszuklappen</li>
<li>Nutzen Sie die Suche um gezielt zu filtern</li>
<li>Referenznummern sind hervorgehoben</li>
<li>Endpunkte haben keine Unterelemente</li>
</ul>
</div>
</div>
</div>
</div>
<style>
/* Tree Styles */
.tree-root {
list-style: none;
padding-left: 0;
margin: 0;
}
.tree-node {
margin-bottom: 2px;
}
.tree-node-content {
display: flex;
align-items: center;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
border: 1px solid transparent;
}
.tree-node-content:hover {
background-color: var(--bg-secondary);
border-color: var(--primary-color);
}
.tree-toggle {
width: 20px;
text-align: center;
font-size: 12px;
color: var(--text-muted);
margin-right: 8px;
}
.tree-toggle-placeholder {
width: 20px;
margin-right: 8px;
}
.tree-link {
text-decoration: none;
color: var(--text-primary);
flex: 1;
display: flex;
align-items: center;
gap: 8px;
}
.tree-link:hover {
color: var(--primary-color);
}
.tree-number {
font-family: var(--font-mono);
font-weight: 600;
color: var(--primary-color);
background-color: var(--bg-secondary);
padding: 2px 6px;
border-radius: 4px;
font-size: 0.875rem;
}
.tree-text {
font-weight: 500;
}
.tree-node-meta {
margin-left: auto;
display: flex;
gap: 4px;
}
.tree-children {
list-style: none;
padding-left: 28px;
margin: 2px 0 0 0;
border-left: 2px solid var(--border-color);
margin-left: 10px;
}
.tree-children .tree-node {
position: relative;
}
.tree-children .tree-node::before {
content: '';
position: absolute;
left: -30px;
top: 20px;
width: 20px;
height: 1px;
background-color: var(--border-color);
}
/* Collapsed state */
.tree-children.collapsed {
display: none;
}
.tree-node.collapsed .tree-toggle {
transform: rotate(-90deg);
}
/* Highlighted search results */
.tree-node.highlighted > .tree-node-content {
background-color: var(--warning-color);
color: white;
}
.tree-node.highlighted .tree-number {
background-color: rgba(255, 255, 255, 0.2);
color: white;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.tree-children {
padding-left: 20px;
}
.tree-node-meta {
display: none;
}
.tree-number {
font-size: 0.75rem;
}
}
</style>
<script>
let currentMatchIndex = -1;
let matches = [];
function toggleNode(element) {
const node = element.parentElement;
const children = node.querySelector(':scope > .tree-children');
const toggle = element.querySelector('.tree-toggle');
if (children) {
children.classList.toggle('collapsed');
node.classList.toggle('collapsed');
if (toggle) {
toggle.textContent = children.classList.contains('collapsed') ? '▶' : '▼';
}
}
}
function expandAll() {
const children = document.querySelectorAll('.tree-children');
const nodes = document.querySelectorAll('.tree-node');
const toggles = document.querySelectorAll('.tree-toggle');
children.forEach(child => child.classList.remove('collapsed'));
nodes.forEach(node => node.classList.remove('collapsed'));
toggles.forEach(toggle => {
if (toggle.textContent === '▶') {
toggle.textContent = '▼';
}
});
}
function collapseAll() {
const children = document.querySelectorAll('.tree-children');
const nodes = document.querySelectorAll('.tree-node');
const toggles = document.querySelectorAll('.tree-toggle');
children.forEach(child => child.classList.add('collapsed'));
nodes.forEach(node => node.classList.add('collapsed'));
toggles.forEach(toggle => {
if (toggle.textContent === '▼') {
toggle.textContent = '▶';
}
});
}
function filterTree() {
const searchTerm = document.getElementById('tree-search').value.toLowerCase();
const filterType = document.getElementById('tree-filter').value;
const nodes = document.querySelectorAll('.tree-node');
matches = [];
currentMatchIndex = -1;
nodes.forEach(node => {
const nodeText = node.dataset.nodeText.toLowerCase();
const hasChildren = node.querySelector(':scope > .tree-children') !== null;
const isLeaf = !hasChildren;
let showNode = true;
// Apply search filter
if (searchTerm && !nodeText.includes(searchTerm)) {
showNode = false;
}
// Apply type filter
if (filterType === 'has-children' && !hasChildren) {
showNode = false;
} else if (filterType === 'leaf-only' && !isLeaf) {
showNode = false;
}
// Show/hide node
node.style.display = showNode ? 'block' : 'none';
// Highlight search matches
if (searchTerm && nodeText.includes(searchTerm)) {
node.classList.add('highlighted');
matches.push(node);
} else {
node.classList.remove('highlighted');
}
// Auto-expand parent nodes of matches
if (searchTerm && nodeText.includes(searchTerm)) {
let parent = node.parentElement;
while (parent && parent.classList.contains('tree-children')) {
parent.classList.remove('collapsed');
parent.parentElement.classList.remove('collapsed');
const toggle = parent.parentElement.querySelector('.tree-toggle');
if (toggle) toggle.textContent = '▼';
parent = parent.parentElement.parentElement;
}
}
});
}
function findNextMatch() {
if (matches.length === 0) return;
currentMatchIndex = (currentMatchIndex + 1) % matches.length;
const match = matches[currentMatchIndex];
// Scroll to match
match.scrollIntoView({ behavior: 'smooth', block: 'center' });
// Highlight temporarily
match.style.backgroundColor = 'var(--accent-color)';
setTimeout(() => {
match.style.backgroundColor = '';
}, 1000);
}
function scrollToTop() {
document.getElementById('tree-container').scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
function resetFilters() {
document.getElementById('tree-search').value = '';
document.getElementById('tree-filter').value = '';
filterTree();
expandAll();
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
// Add keyboard shortcuts
document.addEventListener('keydown', function(e) {
if (e.ctrlKey || e.metaKey) {
switch(e.key) {
case 'f':
e.preventDefault();
document.getElementById('tree-search').focus();
break;
case 'e':
e.preventDefault();
expandAll();
break;
case 'w':
e.preventDefault();
collapseAll();
break;
}
}
});
});
</script>
{% endblock %}