feat: implement Phase 2 (Pulse, Admin, RC2)
This commit is contained in:
82
app/templates/admin/config_editor.html
Normal file
82
app/templates/admin/config_editor.html
Normal file
@@ -0,0 +1,82 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Admin Config Editor{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="container">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li><a href="/admin">Admin Dashboard</a></li>
|
||||
<li>Config Editor</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<hgroup>
|
||||
<h1>🛠️ Config Editor</h1>
|
||||
<p>System Configuration (JSON). Be careful!</p>
|
||||
</hgroup>
|
||||
|
||||
{% if error %}
|
||||
<article class="pico-background-pumpkin-500" style="color: white; border-color: red;">
|
||||
<header>⚠️ Error</header>
|
||||
<pre>{{ error }}</pre>
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" action="/admin/config/save" id="configForm">
|
||||
<label for="config_json">
|
||||
crumbforest_config.json
|
||||
<textarea id="config_json" name="config_json" rows="25"
|
||||
style="font-family: monospace; font-size: 0.9em; white-space: pre;"
|
||||
spellcheck="false">{{ config_json }}</textarea>
|
||||
</label>
|
||||
|
||||
<script>
|
||||
// Simple auto-indent on load if not already indented?
|
||||
// Actually config_json from backend is likely raw string provided by ConfigService.get_raw_config()
|
||||
// We can try to format it nicely in JS on load
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const ta = document.getElementById('config_json');
|
||||
try {
|
||||
const obj = JSON.parse(ta.value);
|
||||
ta.value = JSON.stringify(obj, null, 4);
|
||||
} catch (e) {
|
||||
console.warn("Could not auto-format JSON on load", e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="grid">
|
||||
<button type="button" class="secondary outline" onclick="window.history.back()">Cancel</button>
|
||||
<button type="submit" id="saveBtn">💾 Save Configuration</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const form = document.getElementById('configForm');
|
||||
const ta = document.getElementById('config_json');
|
||||
|
||||
form.addEventListener('submit', (e) => {
|
||||
try {
|
||||
JSON.parse(ta.value);
|
||||
// Valid JSON
|
||||
} catch (err) {
|
||||
e.preventDefault();
|
||||
alert("❌ INVALID JSON!\n\nPlease fix the syntax error:\n" + err.message);
|
||||
}
|
||||
});
|
||||
|
||||
// Tab support
|
||||
ta.addEventListener('keydown', function (e) {
|
||||
if (e.key == 'Tab') {
|
||||
e.preventDefault();
|
||||
var start = this.selectionStart;
|
||||
var end = this.selectionEnd;
|
||||
this.value = this.value.substring(0, start) +
|
||||
" " + this.value.substring(end);
|
||||
this.selectionStart = this.selectionEnd = start + 4;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</main>
|
||||
{% endblock %}
|
||||
129
app/templates/crumbforest/blog.html
Normal file
129
app/templates/crumbforest/blog.html
Normal file
@@ -0,0 +1,129 @@
|
||||
{% extends group_config.template_base or "base_demo.html" %}
|
||||
|
||||
{% block title %}Crumbforest Pulse{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="container">
|
||||
<hgroup>
|
||||
<h1>📰 Crumbforest Pulse</h1>
|
||||
<p>Neuigkeiten, Wissen und Geschichten aus dem Wald.</p>
|
||||
</hgroup>
|
||||
|
||||
<!-- Tag Cloud -->
|
||||
<section class="tag-cloud-container">
|
||||
<h6>Themenwolke</h6>
|
||||
<div class="tag-cloud">
|
||||
<a href="/crumbforest/pulse" class="tag-pill {% if not current_tag %}active{% endif %}">
|
||||
Alle
|
||||
</a>
|
||||
{% for tag in tag_cloud %}
|
||||
<a href="/crumbforest/pulse?tag={{ tag.name }}" class="tag-pill {% if tag.active %}active{% endif %}"
|
||||
style="font-size: {{ tag.size }}; opacity: {{ '1' if tag.active or not current_tag else '0.6' }}">
|
||||
{{ tag.name }} <small>({{ tag.count }})</small>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Articles Grid -->
|
||||
<div class="articles-grid">
|
||||
{% for post in posts %}
|
||||
<article class="post-card">
|
||||
<header>
|
||||
<small>{{ post.created_at.strftime('%d.%m.%Y') }} • ✍️ {{ post.author }}</small>
|
||||
<h3>
|
||||
<a href="/crumbforest/pulse/{{ post.slug }}" class="contrast">{{ post.title }}</a>
|
||||
</h3>
|
||||
</header>
|
||||
|
||||
<p>{{ post.excerpt }}</p>
|
||||
|
||||
<footer>
|
||||
<div class="tags">
|
||||
{% for t in post.tags %}
|
||||
<span class="badge">#{{ t }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<a href="/crumbforest/pulse/{{ post.slug }}" role="button" class="outline">
|
||||
Lesen →
|
||||
</a>
|
||||
</footer>
|
||||
</article>
|
||||
{% else %}
|
||||
<article>
|
||||
<p>Keine Artikel gefunden. 🍂</p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.tag-cloud-container {
|
||||
background: var(--card-background-color);
|
||||
padding: 1rem;
|
||||
border-radius: var(--border-radius);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.tag-cloud {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.tag-pill {
|
||||
text-decoration: none;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 99px;
|
||||
background-color: var(--secondary);
|
||||
color: white;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tag-pill:hover,
|
||||
.tag-pill.active {
|
||||
background-color: var(--primary);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.tag-pill.active {
|
||||
box-shadow: 0 0 10px var(--primary-focus);
|
||||
}
|
||||
|
||||
.articles-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.articles-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-size: 0.7em;
|
||||
padding: 2px 6px;
|
||||
background-color: var(--card-sectionning-background-color);
|
||||
border-radius: 4px;
|
||||
color: var(--contrast);
|
||||
}
|
||||
|
||||
.post-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
67
app/templates/crumbforest/post.html
Normal file
67
app/templates/crumbforest/post.html
Normal file
@@ -0,0 +1,67 @@
|
||||
{% extends group_config.template_base or "base_demo.html" %}
|
||||
|
||||
{% block title %}{{ post.title }} - Pulse{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="container">
|
||||
<div style="max-width: 800px; margin: 0 auto;">
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li><a href="/crumbforest/pulse">📰 Pulse</a></li>
|
||||
<li>{{ post.title }}</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<h1>{{ post.title }}</h1>
|
||||
<small>
|
||||
Veröffentlicht am {{ post.created_at.strftime('%d.%m.%Y') }}
|
||||
von <strong>{{ post.author }}</strong>
|
||||
</small>
|
||||
<div class="tags" style="margin-top: 0.5rem;">
|
||||
{% for t in post.tags %}
|
||||
<span class="badge">#{{ t }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="post-content">
|
||||
{{ post.html_content | safe }}
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<hr>
|
||||
<a href="/crumbforest/pulse" role="button" class="secondary outline">
|
||||
← Zurück zur Übersicht
|
||||
</a>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-size: 0.8em;
|
||||
padding: 2px 8px;
|
||||
background-color: var(--card-sectionning-background-color);
|
||||
border-radius: 99px;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.post-content h2 {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
51
app/templates/pages/doc_viewer.html
Normal file
51
app/templates/pages/doc_viewer.html
Normal file
@@ -0,0 +1,51 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ doc_title }} - Docs{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="container">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li><a href="/docs">📚 Docs</a></li>
|
||||
<li>{{ filename }}</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<article class="doc-content">
|
||||
{{ doc_content | safe }}
|
||||
|
||||
<footer>
|
||||
<hr>
|
||||
<a href="/docs" role="button" class="secondary outline">← Zurück zur Übersicht</a>
|
||||
</footer>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
/* Styling for rendered Markdown */
|
||||
.doc-content h1,
|
||||
.doc-content h2,
|
||||
.doc-content h3 {
|
||||
color: var(--h1-color);
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.doc-content pre {
|
||||
background: var(--code-background-color);
|
||||
padding: 1em;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.doc-content table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.doc-content th,
|
||||
.doc-content td {
|
||||
border-bottom: 1px solid var(--muted-border-color);
|
||||
padding: 0.5em;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
29
app/templates/pages/docs_index.html
Normal file
29
app/templates/pages/docs_index.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ page_title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="container">
|
||||
<hgroup>
|
||||
<h1>📚 Dokumentation</h1>
|
||||
<p>Startfiles und Handbücher direkt im Browser lesen.</p>
|
||||
</hgroup>
|
||||
|
||||
<div class="grid">
|
||||
{% for doc in docs %}
|
||||
<article>
|
||||
<header>📄 {{ doc.name }}</header>
|
||||
<p><code>{{ doc.file }}</code></p>
|
||||
<footer>
|
||||
<a href="/docs/{{ doc.file }}" role="button" class="outline">Lesen →</a>
|
||||
</footer>
|
||||
</article>
|
||||
{% else %}
|
||||
<p>Keine Dokumentation gefunden. 🤷♂️</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<p><small>Hinweis: Diese Dateien liegen im Root-Verzeichnis des Servers.</small></p>
|
||||
</main>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user