Quelltext des aktuellen Standes
<?php
// Standardwerte setzen
$query = $query ?? '';
$sort = $sort ?? 'relevance';
$total = $total ?? 0;
$total_pages = $total_pages ?? 1;
$page = $page ?? 1;
$selected_topic = $selected_topic ?? null;
$selected_topic_name = $selected_topic_name ?? '';
$topics = $topics ?? [];
$results = $results ?? [];
$topic_matches = $topic_matches ?? [];
$module_groups = $module_groups ?? [];
$this->layout('layouts/main', [
'page_title' => 'Suche' . ($query ? ': ' . $query : ''),
'full_width' => true
]);
?>
<div class="search-fullwidth">
<!-- Content Header -->
<div class="content-header">
<div>
<h1><?= $this->te('Suche') ?></h1>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
<li class="breadcrumb-item active"><?= $this->te('Suche') ?></li>
</ol>
</div>
</div>
<!-- Body -->
<div class="srch-body">
<!-- Search Card -->
<div class="card card-outline card-primary mb-3">
<div class="filter-card-body">
<form action="/search" method="GET">
<div class="input-group mb-2">
<input type="search" name="q" value="<?= $this->e($query) ?>"
placeholder="<?= $this->te('Suchbegriff eingeben…') ?>"
class="form-control"
autocomplete="off" autofocus>
<div class="input-group-append">
<button type="submit" class="btn btn-primary"><?= $this->te('🔍 Suchen') ?></button>
</div>
</div>
<div class="srch-filters">
<div class="srch-filter-item">
<label for="f-359f-topic_id" class="srch-filter-label"><?= $this->te('Themengebiet:') ?></label>
<select id="f-359f-topic_id" name="topic_id" class="form-control form-control-sm" style="width:auto;">
<option value=""><?= $this->te('Alle Themengebiete') ?></option>
<?php foreach ($topics as $t): ?>
<?php $indent = str_repeat('— ', $t['depth'] ?? 0); ?>
<option value="<?= $t['id'] ?>" <?= ($selected_topic == $t['id']) ? 'selected' : '' ?>>
<?= $indent ?><?= $this->e($t['name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="srch-filter-item">
<label for="f-359f-sort" class="srch-filter-label"><?= $this->te('Sortierung:') ?></label>
<select id="f-359f-sort" name="sort" class="form-control form-control-sm" style="width:auto;">
<option value="relevance" <?= ($sort === 'relevance') ? 'selected' : '' ?>><?= $this->te('Relevanz') ?></option>
<option value="date" <?= ($sort === 'date') ? 'selected' : '' ?>><?= $this->te('Datum') ?></option>
<option value="title" <?= ($sort === 'title') ? 'selected' : '' ?>><?= $this->te('Titel') ?></option>
</select>
</div>
</div>
</form>
</div>
</div>
<?php if (!empty($query)): ?>
<?php
$hasModules = !empty($module_groups);
$hasTopics = !empty($topic_matches);
$hasPages = !empty($results);
$hasAnything = $hasModules || $hasTopics || $hasPages;
?>
<?php if (!$hasAnything): ?>
<!-- Keine Ergebnisse -->
<div class="card">
<div class="card-body text-center srch-empty">
<div class="srch-empty-icon">😕</div>
<h4><?= $this->te('Keine Ergebnisse') ?></h4>
<p class="text-muted">Für „<strong><?= $this->e($query) ?></strong>" wurden keine Ergebnisse gefunden.</p>
<ul class="text-left d-inline-block text-muted" style="font-size:0.875rem;">
<li><?= $this->te('Verwenden Sie weniger oder allgemeinere Suchbegriffe') ?></li>
<li><?= $this->te('Prüfen Sie die Rechtschreibung') ?></li>
<li><?= $this->te('Suchen Sie in allen Themengebieten') ?></li>
</ul>
</div>
</div>
<?php else: ?>
<p class="srch-summary">
Ergebnisse für „<strong><?= $this->e($query) ?></strong>"<?php if ($selected_topic): ?> in <span class="text-primary"><?= $this->e($selected_topic_name ?? '') ?></span><?php endif; ?>
</p>
<!-- Modulübergreifende Treffer (Service-gesteuert) -->
<?php foreach ($module_groups as $g): ?>
<div class="card card-outline card-primary mb-3">
<div class="card-header">
<h3 class="card-title"><span class="mr-1"><?= $g['icon'] ?></span> <?= $this->e($g['label']) ?></h3>
<div class="card-tools"><span class="badge badge-primary"><?= (int)$g['total'] ?></span></div>
</div>
<div class="card-body p-0">
<?php foreach ($g['items'] as $it): ?>
<a href="<?= $this->e($it['url']) ?>" class="srch-row">
<div class="srch-row-icon"><span class="srch-emoji"><?= $g['icon'] ?></span></div>
<div class="srch-row-body">
<div class="srch-row-title"><?= $this->highlightSearch($it['title'] ?? '', $query) ?></div>
<?php if (!empty($it['subtitle'])): ?>
<div class="srch-row-meta"><span><?= $this->e($it['subtitle']) ?></span></div>
<?php endif; ?>
</div>
<div class="srch-row-arrow">›</div>
</a>
<?php endforeach; ?>
<?php if (!empty($g['has_more'])): ?>
<a href="<?= $this->e($g['more_url']) ?>" class="srch-row srch-row-more">
<div class="srch-row-body"><div class="srch-row-title text-primary"><?= $this->t('Alle') ?> <?= (int)$g['total'] ?> Treffer anzeigen →</div></div>
</a>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<!-- 2. Themengebiete -->
<?php if ($hasTopics): ?>
<div class="card card-outline card-primary mb-3">
<div class="card-header">
<h3 class="card-title">
<i class="fas fa-folder mr-1"></i> <?= $this->te('Themengebiete') ?>
</h3>
<div class="card-tools">
<span class="badge badge-primary"><?= count($topic_matches) ?></span>
</div>
</div>
<div class="card-body p-0">
<?php foreach ($topic_matches as $tm): ?>
<a href="/topics/<?= $tm['id'] ?>" class="srch-row">
<div class="srch-row-icon">
<?php if (!empty($tm['image_path'])): ?>
<img src="/uploads/topics/<?= $this->e($tm['image_path']) ?>" alt="" class="srch-avatar srch-avatar-sq">
<?php else: ?>
<span class="srch-emoji">📁</span>
<?php endif; ?>
</div>
<div class="srch-row-body">
<div class="srch-row-title"><?= $this->highlightSearch($tm['name'], $query) ?></div>
<div class="srch-row-meta">
<span><?= (int)$tm['page_count'] ?> Seite<?= (int)$tm['page_count'] !== 1 ? 'n' : '' ?></span>
<?php if (!empty($tm['description'])): ?>
<span><?= $this->e(mb_substr($tm['description'], 0, 100)) ?><?= mb_strlen($tm['description'] ?? '') > 100 ? '…' : '' ?></span>
<?php endif; ?>
</div>
</div>
<div class="srch-row-arrow">›</div>
</a>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<!-- 3. Seiten / Dateien -->
<?php if ($hasPages): ?>
<?php $isTagSearch = str_starts_with($query, 'tag:'); ?>
<div class="card card-outline card-primary mb-3">
<div class="card-header">
<h3 class="card-title">
<i class="fas fa-file-alt mr-1"></i>
<?= $isTagSearch ? 'Seiten mit Tag' : 'Seiten' ?>
</h3>
<div class="card-tools">
<span class="badge badge-primary"><?= $total ?></span>
</div>
</div>
<div class="card-body p-0">
<?php foreach ($results as $result): ?>
<?php $isFile = (($result['result_type'] ?? 'page') === 'file'); ?>
<a href="<?= $isFile ? '/files/' . $result['id'] : '/pages/' . $result['id'] ?>" class="srch-row">
<div class="srch-row-icon">
<span class="srch-emoji"><?= $isFile ? '📎' : '📄' ?></span>
</div>
<div class="srch-row-body">
<div class="srch-row-title"><?= $this->highlightSearch($result['title'] ?? '', $isTagSearch ? '' : $query) ?></div>
<?php if (!empty($result['snippet'])): ?>
<div class="srch-row-snippet"><?= $this->highlightSearch($result['snippet'], $isTagSearch ? '' : $query) ?></div>
<?php endif; ?>
<div class="srch-row-meta">
<?php if (!empty($result['topic_name'])): ?>
<span>📁 <?= $this->e($result['topic_name']) ?></span>
<?php endif; ?>
<?php if (!empty($result['version_number'])): ?>
<span>v<?= $result['version_number'] ?></span>
<?php endif; ?>
<?php if (!empty($result['updated_at'])): ?>
<span><?= $this->relativeTime($result['updated_at']) ?></span>
<?php endif; ?>
<?php if (!empty($result['tags'])): ?>
<?php foreach ($result['tags'] as $rtag): ?>
<span class="srch-tag" style="background:<?= $this->e($rtag['color']) ?>18;color:<?= $this->e($rtag['color']) ?>;border:1px solid <?= $this->e($rtag['color']) ?>35;"
onclick="event.stopPropagation();window.location='/search?q=tag%3A<?= urlencode($rtag['name']) ?>';">
<?= $this->e($rtag['name']) ?>
</span>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<div class="srch-row-arrow">›</div>
</a>
<?php endforeach; ?>
</div>
<!-- Pagination -->
<?php if ($total_pages > 1): ?>
<div class="card-footer">
<nav>
<ul class="pagination pagination-sm m-0">
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
<a aria-label="<?= $this->te('Vorherige Seite') ?>" class="page-link" href="?q=<?= urlencode($query) ?>&page=<?= $page - 1 ?>&topic_id=<?= $selected_topic ?>&sort=<?= urlencode($sort) ?>">«</a>
</li>
<?php
$start = max(1, $page - 2);
$end = min($total_pages, $page + 2);
if ($start > 1): ?>
<li class="page-item"><a class="page-link" href="?q=<?= urlencode($query) ?>&page=1&topic_id=<?= $selected_topic ?>&sort=<?= urlencode($sort) ?>">1</a></li>
<?php if ($start > 2): ?><li class="page-item disabled"><span class="page-link">…</span></li><?php endif; ?>
<?php endif; ?>
<?php for ($i = $start; $i <= $end; $i++): ?>
<li class="page-item <?= $i === $page ? 'active' : '' ?>">
<a class="page-link" href="?q=<?= urlencode($query) ?>&page=<?= $i ?>&topic_id=<?= $selected_topic ?>&sort=<?= urlencode($sort) ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
<?php if ($end < $total_pages): ?>
<?php if ($end < $total_pages - 1): ?><li class="page-item disabled"><span class="page-link">…</span></li><?php endif; ?>
<li class="page-item"><a class="page-link" href="?q=<?= urlencode($query) ?>&page=<?= $total_pages ?>&topic_id=<?= $selected_topic ?>&sort=<?= urlencode($sort) ?>"><?= $total_pages ?></a></li>
<?php endif; ?>
<li class="page-item <?= $page >= $total_pages ? 'disabled' : '' ?>">
<a aria-label="<?= $this->te('Nächste Seite') ?>" class="page-link" href="?q=<?= urlencode($query) ?>&page=<?= $page + 1 ?>&topic_id=<?= $selected_topic ?>&sort=<?= urlencode($sort) ?>">»</a>
</li>
</ul>
</nav>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php endif; ?>
<?php else: ?>
<!-- Startseite Suche -->
<div class="card card-outline card-primary">
<div class="card-body text-center srch-empty">
<div class="srch-empty-icon">🔍</div>
<h4><?= $this->te('Dokumentation durchsuchen') ?></h4>
<p class="text-muted"><?= $this->te('Geben Sie einen Suchbegriff ein, um OpenNIT zu durchsuchen.') ?></p>
<?php if (!empty($topics)): ?>
<div class="srch-topic-tags">
<p class="text-muted mb-1" style="font-size:0.85rem;"><?= $this->te('Beliebte Themengebiete:') ?></p>
<?php foreach (array_slice($topics, 0, 8) as $t): ?>
<?php if (($t['depth'] ?? 0) == 0): ?>
<a href="/topics/<?= $t['id'] ?>" class="badge badge-light srch-topic-badge"><?= $this->e($t['name']) ?></a>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</div><!-- /.srch-body -->
</div>
<style>
/* ── Layout ─────────────────────────────────────────────────────────────── */
.srch-body {
padding: 1.5rem 1rem 3rem;
background: var(--slate-50);
min-height: calc(100vh - 320px);
}
.srch-summary {
font-size: 0.9rem;
color: var(--gray-500);
margin: 0 0 1rem 0;
}
.srch-filters {
display: flex;
gap: 1.25rem;
flex-wrap: wrap;
align-items: center;
}
.srch-filter-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
.srch-filter-label {
margin: 0;
font-size: 0.82rem;
color: var(--color-secondary);
white-space: nowrap;
}
/* ── Result rows ────────────────────────────────────────────────────────── */
.srch-row {
display: flex;
align-items: flex-start;
gap: 0.875rem;
padding: 0.75rem 1rem;
text-decoration: none;
color: inherit;
border-bottom: 1px solid var(--gray-100);
transition: background 0.1s;
}
.srch-row:last-child { border-bottom: none; }
.srch-row:hover { background: var(--slate-50); text-decoration: none; color: inherit; }
.srch-row-icon {
flex-shrink: 0;
width: 34px;
height: 34px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 0.1rem;
}
.srch-avatar {
width: 34px; height: 34px;
border-radius: 50%; object-fit: cover;
}
.srch-avatar-sq { border-radius: 0.25rem; }
.srch-emoji { font-size: 1.35rem; line-height: 1; }
.srch-row-body { flex: 1; min-width: 0; }
.srch-row-title {
font-weight: 600;
font-size: 0.9rem;
color: var(--color-primary-deep);
margin-bottom: 0.2rem;
}
.srch-row-title mark {
background: var(--alert-warning-bg); color: inherit;
padding: 0 2px; border-radius: 2px;
}
.srch-row-snippet {
font-size: 0.78rem;
color: var(--gray-500);
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
margin-bottom: 0.2rem;
}
.srch-row-snippet mark {
background: var(--alert-warning-bg); color: inherit;
padding: 0 2px; border-radius: 2px;
}
.srch-row-meta {
display: flex;
align-items: center;
gap: 0.4rem;
flex-wrap: wrap;
font-size: 0.72rem;
color: var(--gray-400);
}
.srch-row-meta span + span:not(.badge):not(.srch-tag)::before {
content: "·";
margin-right: 0.4rem;
}
.srch-tag {
display: inline-block;
padding: 0.1rem 0.45rem;
border-radius: 0.25rem;
font-size: 0.68rem;
font-weight: 600;
cursor: pointer;
}
.srch-row-arrow {
flex-shrink: 0;
color: var(--gray-300);
font-size: 1.1rem;
align-self: center;
}
/* ── Empty / start states ───────────────────────────────────────────────── */
.srch-empty { padding: 2.5rem 1rem; }
.srch-empty-icon { font-size: 3.5rem; margin-bottom: 0.75rem; opacity: 0.7; }
.srch-topic-tags { margin-top: 1rem; display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: center; }
.srch-topic-badge {
font-size: 0.82rem; font-weight: 500;
padding: 0.35rem 0.75rem;
border: 1px solid var(--color-gray-300);
border-radius: 0.25rem;
text-decoration: none;
}
.srch-topic-badge:hover { background: var(--color-gray-200); text-decoration: none; }
@media (max-width: 768px) {
.srch-body { padding: 1rem; }
.srch-filters { gap: 0.75rem; }
}
</style>