Initial commit - Help Service for Coolify
This commit is contained in:
241
templates/topic.html
Normal file
241
templates/topic.html
Normal file
@@ -0,0 +1,241 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ topic.title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/">Startseite</a></li>
|
||||
{% if parent_topic %}
|
||||
<li class="breadcrumb-item"><a href="/topic/{{ parent_topic.id }}">{{ parent_topic.title }}</a></li>
|
||||
{% endif %}
|
||||
<li class="breadcrumb-item active">{{ topic.title }}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<!-- Topic Header -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<i class="bi bi-{{ topic.icon|default('file-text') }} text-primary display-4 me-3"></i>
|
||||
<div>
|
||||
<h1 class="mb-1">{{ topic.title }}</h1>
|
||||
<p class="text-muted mb-0">{{ topic.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if topic.tags %}
|
||||
<div class="mt-3">
|
||||
{% for tag in topic.tags %}
|
||||
<span class="badge bg-secondary me-1">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Topic Content -->
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div class="card">
|
||||
<div class="card-body content-body">
|
||||
{{ topic.content|safe }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Child Topics / Subpages -->
|
||||
{% if child_topics %}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header bg-primary bg-opacity-10">
|
||||
<h5 class="mb-0"><i class="bi bi-folder2-open me-2"></i>Unterseiten</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
{% for child in child_topics %}
|
||||
<div class="col-md-6 mb-3">
|
||||
<a href="/topic/{{ child.id }}" class="text-decoration-none">
|
||||
<div class="card h-100 hover-shadow">
|
||||
<div class="card-body py-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-{{ child.icon|default('file-text') }} text-primary me-3 fs-4"></i>
|
||||
<div>
|
||||
<h6 class="mb-1">{{ child.title }}</h6>
|
||||
{% if child.description %}
|
||||
<small class="text-muted">{{ child.description }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Related Topics -->
|
||||
{% if topic.related %}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="bi bi-link-45deg me-2"></i>Verwandte Themen</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
{% for related_id in topic.related %}
|
||||
{% for section in content.sections %}
|
||||
{% for t in section.topics %}
|
||||
{% if t.id == related_id %}
|
||||
<div class="col-md-6 mb-2">
|
||||
<a href="/topic/{{ t.id }}" class="text-decoration-none">
|
||||
<div class="d-flex align-items-center p-2 rounded hover-bg">
|
||||
<i class="bi bi-{{ t.icon|default('file-text') }} text-primary me-2"></i>
|
||||
<span>{{ t.title }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Sidebar (Sticky) -->
|
||||
<div class="col-lg-3">
|
||||
<div class="toc-sidebar">
|
||||
<!-- Table of Contents -->
|
||||
{% if topic.toc %}
|
||||
<div class="card mb-4 toc-card">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0"><i class="bi bi-list-ul me-2"></i>Inhalt</h6>
|
||||
</div>
|
||||
<div class="card-body py-2">
|
||||
<nav class="toc-nav" id="toc-nav">
|
||||
{% for item in topic.toc %}
|
||||
<a href="#{{ item.id }}" class="toc-link {% if item.level == 3 %}toc-link-sub{% endif %}" data-target="{{ item.id }}">
|
||||
{{ item.title }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Quick Tips -->
|
||||
{% if topic.tips %}
|
||||
<div class="card">
|
||||
<div class="card-header bg-success bg-opacity-25">
|
||||
<h6 class="mb-0 text-success"><i class="bi bi-lightbulb me-2"></i>Tipps</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-unstyled mb-0">
|
||||
{% for tip in topic.tips %}
|
||||
<li class="mb-2">
|
||||
<i class="bi bi-check-circle text-success me-1"></i>
|
||||
{{ tip }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between">
|
||||
<a href="/" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left me-2"></i>Zurueck zur Uebersicht
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// Scroll-Spy for TOC
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tocNav = document.getElementById('toc-nav');
|
||||
if (!tocNav) return;
|
||||
|
||||
const tocLinks = tocNav.querySelectorAll('.toc-link');
|
||||
if (tocLinks.length === 0) return;
|
||||
|
||||
// Get all headings that are in the TOC
|
||||
const headingIds = Array.from(tocLinks).map(link => link.getAttribute('data-target'));
|
||||
const headings = headingIds.map(id => document.getElementById(id)).filter(el => el);
|
||||
|
||||
if (headings.length === 0) return;
|
||||
|
||||
function updateActiveLink() {
|
||||
const scrollPos = window.scrollY + 100; // Offset for better UX
|
||||
|
||||
let activeIndex = 0;
|
||||
|
||||
// Find the heading that is currently in view
|
||||
for (let i = 0; i < headings.length; i++) {
|
||||
if (headings[i].offsetTop <= scrollPos) {
|
||||
activeIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Update active state
|
||||
tocLinks.forEach((link, index) => {
|
||||
if (index === activeIndex) {
|
||||
link.classList.add('active');
|
||||
} else {
|
||||
link.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Throttle scroll events
|
||||
let ticking = false;
|
||||
window.addEventListener('scroll', function() {
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(function() {
|
||||
updateActiveLink();
|
||||
ticking = false;
|
||||
});
|
||||
ticking = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Initial update
|
||||
updateActiveLink();
|
||||
|
||||
// Smooth scroll with offset when clicking TOC links
|
||||
tocLinks.forEach(link => {
|
||||
link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const targetId = this.getAttribute('data-target');
|
||||
const target = document.getElementById(targetId);
|
||||
if (target) {
|
||||
const offset = 20;
|
||||
const targetPos = target.offsetTop - offset;
|
||||
window.scrollTo({
|
||||
top: targetPos,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user