Quelltext des aktuellen Standes
<?php
// Ancestors filtern - aktuelles Topic ausschließen
$filteredAncestors = array_filter($ancestors ?? [], fn($a) => $a['id'] != $topic['id']);
$this->layout('layouts/main', [
'page_title' => $topic['name'],
'breadcrumbs' => [], // Eigener Breadcrumb im Hero
'full_width' => true
]);
$hasImage = !empty($topic['image_path']);
?>
<div class="topic-show-fullwidth">
<!-- Content Header -->
<div class="content-header">
<div>
<h1><?= $this->e($topic['name']) ?></h1>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
<li class="breadcrumb-item"><a href="/topics"><?= $this->te('Themengebiete') ?></a></li>
<?php foreach ($filteredAncestors as $ancestor): ?>
<li class="breadcrumb-item"><a href="/topics/<?= $ancestor['id'] ?>"><?= $this->e($ancestor['name']) ?></a></li>
<?php endforeach; ?>
<li class="breadcrumb-item active"><?= $this->e($topic['name']) ?></li>
</ol>
</div>
<div class="content-header-actions">
<?php if ($this->hasCapabilityForTopic('create_page', $topic['id'])): ?>
<a href="/pages/create?topic_id=<?= $topic['id'] ?>" class="btn btn-primary">
<i class="fas fa-plus"></i>
<?= $this->te('Neue Seite') ?>
</a>
<a href="/topics/create?parent_id=<?= $topic['id'] ?>" class="btn btn-default">
<i class="fas fa-plus"></i>
<?= $this->te('Unterthema') ?>
</a>
<?php endif; ?>
<?php if ($this->hasCapability('edit_page')): ?>
<a href="/topics/<?= $topic['id'] ?>/edit" class="btn btn-default">
<i class="fas fa-pencil-alt"></i> <?= $this->te('Bearbeiten') ?>
</a>
<?php endif; ?>
</div>
</div>
<div class="topic-body">
<?php if (!empty($is_passthrough)): ?>
<div class="alert alert-info">
<i class="fas fa-route"></i>
<?= $this->te('Dieses Themengebiet ist für Sie nur der Weg zu den Unterthemen darunter. Eigene Seiten sehen Sie hier nicht.') ?>
</div>
<?php endif; ?>
<!-- Unterthemen -->
<?php if (!empty($children)): ?>
<section class="topic-section">
<div class="section-header">
<h2><span aria-hidden="true">📁</span> <?= $this->te('Unterthemen') ?></h2>
<span class="count-badge"><?= count($children) ?></span>
</div>
<div class="subtopics-grid">
<?php foreach ($children as $child): ?>
<a href="/topics/<?= $child['id'] ?>" class="subtopic-card">
<?php if (!empty($child['image_path'])): ?>
<div class="subtopic-image">
<img src="/uploads/topics/<?= $this->e($child['image_path']) ?>" alt="">
</div>
<?php else: ?>
<div class="subtopic-icon" aria-hidden="true">📁</div>
<?php endif; ?>
<div class="subtopic-content">
<h3><?= $this->e($child['name']) ?></h3>
<?php if (!empty($child['description'])): ?>
<p><?= $this->e($this->truncate($child['description'], 80)) ?></p>
<?php endif; ?>
<span class="subtopic-meta">
<?= $this->te(((int)($child['total_page_count'] ?? $child['page_count'] ?? 0)) === 1 ? '{anzahl} Seite' : '{anzahl} Seiten', ['anzahl' => (int)($child['total_page_count'] ?? $child['page_count'] ?? 0)]) ?>
</span>
</div>
<span class="subtopic-arrow" aria-hidden="true">→</span>
</a>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<!-- Seiten -->
<?php $currentViewMode = $view_mode ?? 'cards'; ?>
<?php $currentSortMode = $sort_mode ?? 'date_desc'; ?>
<section class="topic-section">
<div class="section-header">
<h2><span aria-hidden="true">📄</span> <?= $this->te('Seiten') ?></h2>
<span class="count-badge"><?= count($pages ?? []) ?></span>
<?php if (!empty($pages)): ?>
<div class="sort-select-wrap">
<label for="sortSelect" class="sort-select-label"><?= $this->te('Sortierung') ?></label>
<select id="sortSelect" class="sort-select" onchange="saveSortMode(this.value)">
<option value="date_desc" <?= $currentSortMode === 'date_desc' ? 'selected' : '' ?>><?= $this->te('🕑 Neueste zuerst') ?></option>
<option value="date_asc" <?= $currentSortMode === 'date_asc' ? 'selected' : '' ?>><?= $this->te('🕑 Älteste zuerst') ?></option>
<option value="title_asc" <?= $currentSortMode === 'title_asc' ? 'selected' : '' ?>>A → Z</option>
<option value="title_desc" <?= $currentSortMode === 'title_desc' ? 'selected' : '' ?>>Z → A</option>
</select>
</div>
<div class="view-toggle" id="viewToggle">
<button class="view-toggle-btn <?= $currentViewMode === 'cards' ? 'active' : '' ?>" data-mode="cards" title="<?= $this->te('Kartenansicht') ?>" aria-label="<?= $this->te('Kartenansicht') ?>">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><rect x="1" y="1" width="6" height="6" rx="1"/><rect x="9" y="1" width="6" height="6" rx="1"/><rect x="1" y="9" width="6" height="6" rx="1"/><rect x="9" y="9" width="6" height="6" rx="1"/></svg>
</button>
<button class="view-toggle-btn <?= $currentViewMode === 'list' ? 'active' : '' ?>" data-mode="list" title="<?= $this->te('Listenansicht') ?>" aria-label="<?= $this->te('Listenansicht') ?>">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><rect x="1" y="2" width="14" height="2.5" rx="0.5"/><rect x="1" y="6.75" width="14" height="2.5" rx="0.5"/><rect x="1" y="11.5" width="14" height="2.5" rx="0.5"/></svg>
</button>
</div>
<?php endif; ?>
</div>
<?php if (!empty($pages)): ?>
<!-- Kartenansicht -->
<div class="pages-grid" id="pagesGrid" style="<?= $currentViewMode === 'list' ? 'display:none' : '' ?>">
<?php foreach ($pages as $page): ?>
<a href="/pages/<?= $page['id'] ?>" class="page-card">
<div class="page-card-header">
<span class="page-icon" aria-hidden="true">📄</span>
<h3><?= $this->e($page['title']) ?></h3>
</div>
<div class="page-card-meta">
<span class="meta-item">
<span class="meta-icon" aria-hidden="true">🕑</span>
<?= $this->relativeTime($page['updated_at'] ?? $page['created_at']) ?>
</span>
<span class="meta-item">
<span class="meta-icon" aria-hidden="true">📝</span>
v<?= $page['version_number'] ?? 1 ?>
</span>
<?php if (!empty($page['file_count']) && (int)$page['file_count'] > 0): ?>
<span class="meta-item">
<span class="meta-icon" aria-hidden="true">📎</span>
<?= (int)$page['file_count'] ?>
</span>
<?php endif; ?>
</div>
<?php if (!empty($page['is_draft'])): ?>
<span class="page-badge"><?= $this->te('Entwurf') ?></span>
<?php endif; ?>
</a>
<?php endforeach; ?>
</div>
<!-- Listenansicht -->
<div class="pages-list" id="pagesList" style="<?= $currentViewMode === 'cards' ? 'display:none' : '' ?>">
<?php foreach ($pages as $page): ?>
<a href="/pages/<?= $page['id'] ?>" class="page-list-item">
<span class="page-list-icon" aria-hidden="true">📄</span>
<span class="page-list-title"><?= $this->e($page['title']) ?></span>
<?php if (!empty($page['is_draft'])): ?>
<span class="page-badge"><?= $this->te('Entwurf') ?></span>
<?php endif; ?>
<span class="page-list-meta">
v<?= $page['version_number'] ?? 1 ?>
·
<?= $this->relativeTime($page['updated_at'] ?? $page['created_at']) ?>
<?php if (!empty($page['file_count']) && (int)$page['file_count'] > 0): ?>
· <?= $this->te('{anzahl} Dateien', ['anzahl' => (int)$page['file_count']]) ?>
<?php endif; ?>
</span>
<span class="page-list-arrow" aria-hidden="true">→</span>
</a>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="empty-state">
<div class="empty-icon" aria-hidden="true">📄</div>
<h3><?= $this->te('Noch keine Seiten') ?></h3>
<p><?= $this->te('Erstellen Sie die erste Dokumentationsseite in diesem Themengebiet.') ?></p>
<?php if ($this->hasCapabilityForTopic('create_page', $topic['id'])): ?>
<a href="/pages/create?topic_id=<?= $topic['id'] ?>" class="btn btn-primary">
<?= $this->te('Erste Seite erstellen') ?>
</a>
<?php endif; ?>
</div>
<?php endif; ?>
</section>
</div>
</div>
<style>
.topic-show-fullwidth {
}
/* Body */
.topic-body {
padding: 2rem;
background: var(--slate-50);
min-height: calc(100vh - 300px);
}
.topic-section {
margin-bottom: 2rem;
}
.section-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1rem;
}
.section-header h2 {
margin: 0;
font-size: 1.1rem;
font-weight: 600;
color: var(--gray-900);
}
.count-badge {
background: var(--gray-200);
color: var(--gray-700);
padding: 0.25rem 0.625rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 600;
}
/* Subtopics Grid */
.subtopics-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1rem;
}
.subtopic-card {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem 1.25rem;
background: var(--color-bg);
border-radius: 0.25rem;
text-decoration: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
transition: all 0.2s;
}
.subtopic-card:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
text-decoration: none;
}
.subtopic-image {
width: 56px;
height: 56px;
border-radius: 0.25rem;
overflow: hidden;
flex-shrink: 0;
}
.subtopic-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.subtopic-icon {
width: 56px;
height: 56px;
background: linear-gradient(135deg, var(--blue-50) 0%, var(--blue-100) 100%);
border-radius: 0.25rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.75rem;
flex-shrink: 0;
}
.subtopic-content {
flex: 1;
min-width: 0;
}
.subtopic-content h3 {
margin: 0 0 0.25rem 0;
font-size: 1rem;
font-weight: 600;
color: var(--gray-900);
}
.subtopic-content p {
margin: 0 0 0.35rem 0;
font-size: 0.8rem;
color: var(--gray-500);
line-height: 1.4;
}
.subtopic-meta {
font-size: 0.75rem;
color: var(--gray-400);
}
.subtopic-arrow {
color: var(--gray-300);
font-size: 1.25rem;
transition: all 0.15s;
}
.subtopic-card:hover .subtopic-arrow {
color: var(--color-primary);
transform: translateX(3px);
}
/* Pages Grid */
.pages-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
}
.page-card {
display: block;
padding: 1.25rem;
background: var(--color-bg);
border-radius: 0.25rem;
text-decoration: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
transition: all 0.2s;
position: relative;
}
.page-card:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
text-decoration: none;
}
.page-card-header {
display: flex;
align-items: flex-start;
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.page-icon {
font-size: 1.5rem;
flex-shrink: 0;
}
.page-card-header h3 {
margin: 0;
font-size: 0.95rem;
font-weight: 600;
color: var(--gray-900);
line-height: 1.4;
}
.page-card:hover h3 {
color: var(--color-primary);
}
.page-card-meta {
display: flex;
gap: 1rem;
}
.page-card-meta .meta-item {
display: flex;
align-items: center;
gap: 0.25rem;
font-size: 0.75rem;
color: var(--gray-400);
}
.page-card-meta .meta-icon {
font-size: 0.85rem;
}
.page-badge {
position: absolute;
top: 0.75rem;
right: 0.75rem;
background: var(--amber-100);
color: var(--amber-800);
padding: 0.2rem 0.5rem;
border-radius: 9999px;
font-size: 0.65rem;
font-weight: 600;
}
/* View Toggle */
.sort-select-wrap {
margin-left: auto;
display: flex;
align-items: center;
gap: 0.4rem;
}
.sort-select-label {
font-size: 0.78rem;
font-weight: 500;
color: var(--gray-500);
margin: 0;
}
.sort-select {
height: 32px;
padding: 0 0.6rem;
background: var(--gray-100);
border: 1px solid var(--gray-200);
border-radius: 0.375rem;
font-size: 0.78rem;
font-weight: 500;
color: var(--gray-700);
cursor: pointer;
outline: none;
transition: border-color 0.15s, box-shadow 0.15s;
appearance: none;
-webkit-appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%236b7280' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.5rem center;
padding-right: 1.75rem;
}
.sort-select:hover { border-color: var(--gray-300); background-color: var(--gray-50); }
.sort-select:focus { border-color: var(--blue-300); box-shadow: 0 0 0 2px rgba(var(--color-primary-rgb), 0.15); }
.view-toggle {
display: flex;
gap: 0.25rem;
background: var(--gray-100);
border-radius: 0.375rem;
padding: 0.2rem;
}
.view-toggle-btn {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 28px;
border: none;
background: transparent;
border-radius: 0.25rem;
color: var(--gray-400);
cursor: pointer;
transition: all 0.15s;
}
.view-toggle-btn:hover { color: var(--gray-500); }
.view-toggle-btn.active {
background: var(--color-bg);
color: var(--color-primary);
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
/* Listenansicht */
.pages-list {
display: flex;
flex-direction: column;
gap: 0.25rem;
background: var(--color-bg);
border-radius: 0.25rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
overflow: hidden;
}
.page-list-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.875rem 1.25rem;
text-decoration: none;
border-bottom: 1px solid var(--gray-100);
transition: background 0.15s;
}
.page-list-item:last-child { border-bottom: none; }
.page-list-item:hover {
background: var(--gray-50);
text-decoration: none;
}
.page-list-icon { font-size: 1.1rem; flex-shrink: 0; }
.page-list-title {
flex: 1;
font-weight: 500;
color: var(--gray-900);
font-size: 0.95rem;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.page-list-item:hover .page-list-title { color: var(--color-primary); }
.page-list-meta {
font-size: 0.75rem;
color: var(--gray-400);
white-space: nowrap;
flex-shrink: 0;
}
.page-list-arrow {
color: var(--gray-300);
font-size: 1rem;
transition: all 0.15s;
flex-shrink: 0;
}
.page-list-item:hover .page-list-arrow {
color: var(--color-primary);
transform: translateX(2px);
}
.empty-state h3 {
margin: 0 0 0.5rem 0;
font-size: 1.1rem;
color: var(--gray-700);
}
/* Responsive */
@media (max-width: 768px) {
.topic-show-fullwidth {
overflow-x: hidden;
}
.topic-body {
padding: 1rem;
}
.subtopics-grid,
.pages-grid {
grid-template-columns: 1fr;
}
.subtopic-card {
padding: 0.875rem 1rem;
}
.page-card {
padding: 1rem;
}
.section-header h2 {
font-size: 1rem;
}
.empty-state {
padding: 2rem 1rem;
}
}
@media (max-width: 480px) {
.topic-body {
padding: 0.75rem;
}
.subtopic-icon,
.subtopic-image {
width: 44px;
height: 44px;
}
.subtopic-icon {
font-size: 1.25rem;
}
.page-list-meta { display: none; }
}
</style>
<script>
(function() {
var toggle = document.getElementById('viewToggle');
if (!toggle) return;
var grid = document.getElementById('pagesGrid');
var list = document.getElementById('pagesList');
var buttons = toggle.querySelectorAll('.view-toggle-btn');
buttons.forEach(function(btn) {
btn.addEventListener('click', function() {
var mode = this.getAttribute('data-mode');
buttons.forEach(function(b) { b.classList.remove('active'); });
this.classList.add('active');
if (mode === 'list') {
grid.style.display = 'none';
list.style.display = '';
} else {
grid.style.display = '';
list.style.display = 'none';
}
// Einstellung per AJAX speichern
var fd = new FormData();
fd.append('key', 'topic_view_mode');
fd.append('value', mode);
fd.append('_csrf', document.querySelector('meta[name="csrf-token"]')?.content || '<?= $this->csrf() ?>');
fetch('/api/user-settings', { method: 'POST', body: fd });
});
});
})();
function saveSortMode(mode) {
var fd = new FormData();
fd.append('mode', mode);
fd.append('_csrf', document.querySelector('meta[name="csrf-token"]')?.content || '<?= $this->csrf() ?>');
fetch('/topics/save-pages-sort', { method: 'POST', body: fd })
.then(function() { window.location.reload(); });
}
</script>