Files
digiserver-v2/app/templates/help.html
T
ske087 7177cdb9ab Add in-app help page with user manual (Romanian)
Adds a /help page served from the dashboard navigation, containing the full
Romanian user manual for operators: dashboard, playlist management, adding
media, removing files from playlists, player management, and the advanced
edited-media view.

Implementation notes
- Manual is pre-rendered to HTML with pandoc and shipped as a static asset
  (app/static/help/_manual_body.html) rather than parsed at runtime. This
  keeps the container free of a Markdown dependency and makes the page render
  identically regardless of what is installed.
- /help is login-protected; the sidebar table of contents is derived from the
  level-2 headings at request time.
- Screenshots are served from app/static/help/screenshots/ (32 images).
- Regenerate after editing the manual:
    documentatie/convert_help_page.sh
    docker compose up -d --build

Also adds
- backup_players_playlists.sh / restore_database.sh for DB backup and restore.
- .gitignore rules so local database backups (real production data), generated
  .docx files and duplicate screenshot copies are never committed.
2026-09-15 16:46:16 +03:00

277 lines
7.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Ajutor Manual de Utilizare{% endblock %}
{% block content %}
<style>
/* ---- Help page: local layout & typography ---- */
.help-wrapper {
display: grid;
grid-template-columns: 280px 1fr;
gap: 2rem;
align-items: start;
}
/* Sidebar cu cuprins */
.help-toc {
position: sticky;
top: 20px;
max-height: calc(100vh - 40px);
overflow-y: auto;
background: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.25rem;
box-shadow: var(--shadow);
}
.help-toc h3 {
font-size: 0.95rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-secondary);
margin-bottom: 0.75rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--border-color);
}
.help-toc ul {
list-style: none;
padding: 0;
margin: 0;
}
.help-toc li { margin-bottom: 0.15rem; }
.help-toc a {
display: block;
padding: 0.4rem 0.6rem;
border-radius: 6px;
color: var(--text-color);
text-decoration: none;
font-size: 0.875rem;
line-height: 1.35;
transition: all 0.15s;
}
.help-toc a:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.help-toc a.active {
background: var(--primary-color);
color: #fff;
font-weight: 500;
}
/* Continutul manualului */
.help-content {
background: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 2rem 2.5rem;
box-shadow: var(--shadow);
line-height: 1.7;
overflow-wrap: break-word;
}
.help-content h1 {
font-size: 1.85rem;
margin: 0 0 1rem;
padding-bottom: 0.75rem;
border-bottom: 3px solid var(--primary-color);
}
.help-content h2 {
font-size: 1.4rem;
margin: 2.25rem 0 0.85rem;
padding-top: 1rem;
color: var(--primary-color);
scroll-margin-top: 20px;
}
.help-content h3 {
font-size: 1.1rem;
margin: 1.5rem 0 0.6rem;
scroll-margin-top: 20px;
}
.help-content h4 {
font-size: 1rem;
margin: 1.25rem 0 0.5rem;
}
.help-content p { margin: 0.7rem 0; }
.help-content ul, .help-content ol { margin: 0.7rem 0 0.7rem 1.5rem; }
.help-content li { margin-bottom: 0.35rem; }
.help-content hr {
border: none;
border-top: 1px solid var(--border-color);
margin: 2rem 0;
}
.help-content a { color: var(--primary-color); }
.help-content code {
background: var(--bg-color);
padding: 0.15rem 0.4rem;
border-radius: 4px;
font-size: 0.875em;
font-family: 'SFMono-Regular', Consolas, monospace;
border: 1px solid var(--border-color);
}
.help-content pre {
background: var(--bg-color);
padding: 1rem;
border-radius: 8px;
overflow-x: auto;
border: 1px solid var(--border-color);
margin: 1rem 0;
}
.help-content pre code {
background: none;
border: none;
padding: 0;
}
.help-content img {
max-width: 100%;
height: auto;
border-radius: 8px;
border: 1px solid var(--border-color);
box-shadow: var(--shadow);
margin: 0.75rem 0;
display: block;
}
.help-content table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
font-size: 0.9rem;
}
.help-content th, .help-content td {
border: 1px solid var(--border-color);
padding: 0.6rem 0.75rem;
text-align: left;
vertical-align: top;
}
.help-content th {
background: var(--bg-color);
font-weight: 600;
}
.help-content tr:nth-child(even) td {
background: color-mix(in srgb, var(--bg-color) 50%, transparent);
}
/* Citat / atentionari */
.help-content blockquote {
border-left: 4px solid var(--primary-color);
background: var(--bg-color);
margin: 1rem 0;
padding: 0.75rem 1rem;
border-radius: 0 8px 8px 0;
}
.help-content blockquote p { margin: 0.3rem 0; }
/* Textul italics al figurilor */
.help-content em { color: var(--text-secondary); font-size: 0.9em; }
/* Back-to-top */
.help-top-btn {
position: fixed;
right: 24px;
bottom: 24px;
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--primary-color);
color: #fff;
border: none;
cursor: pointer;
font-size: 1.25rem;
box-shadow: var(--shadow-lg);
display: none;
align-items: center;
justify-content: center;
transition: transform 0.2s;
z-index: 500;
}
.help-top-btn:hover { transform: translateY(-3px); }
.help-top-btn.visible { display: flex; }
/* Cauta */
.help-search {
width: 100%;
padding: 0.5rem 0.75rem;
margin-bottom: 0.75rem;
border: 1px solid var(--border-color);
border-radius: 6px;
background: var(--bg-color);
color: var(--text-color);
font-size: 0.875rem;
}
.help-search:focus {
outline: none;
border-color: var(--primary-color);
}
@media (max-width: 900px) {
.help-wrapper { grid-template-columns: 1fr; }
.help-toc { position: static; max-height: none; }
.help-content { padding: 1.25rem; }
.help-content h1 { font-size: 1.5rem; }
.help-content h2 { font-size: 1.2rem; }
}
</style>
<div class="container">
<div class="help-wrapper">
<!-- Cuprins / navigare laterala -->
<aside class="help-toc">
<h3>Cuprins</h3>
<input type="text" id="tocSearch" class="help-search"
placeholder="Caută în cuprins..." autocomplete="off">
<ul id="tocList">
{% for item in toc %}
<li>
<a href="#{{ item.anchor }}" data-anchor="{{ item.anchor }}">
{{ item.title }}
</a>
</li>
{% endfor %}
</ul>
</aside>
<!-- Continutul manualului -->
<main class="help-content" id="helpContent">
{{ manual_html | safe }}
</main>
</div>
</div>
<button class="help-top-btn" id="topBtn" title="Sus" onclick="window.scrollTo({top:0,behavior:'smooth'})"></button>
<script>
(function () {
// Cuprins: evidentiaza sectiunea curenta la scroll
var links = Array.prototype.slice.call(document.querySelectorAll('#tocList a'));
var sections = links.map(function (a) {
return document.getElementById(a.dataset.anchor);
}).filter(Boolean);
function onScroll() {
var btn = document.getElementById('topBtn');
if (window.scrollY > 400) { btn.classList.add('visible'); }
else { btn.classList.remove('visible'); }
var pos = window.scrollY + 120;
var current = null;
for (var i = 0; i < sections.length; i++) {
if (sections[i].offsetTop <= pos) { current = sections[i].id; }
}
links.forEach(function (a) {
a.classList.toggle('active', a.dataset.anchor === current);
});
}
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
// Filtrare in cuprins
var search = document.getElementById('tocSearch');
search.addEventListener('input', function () {
var q = this.value.toLowerCase().trim();
links.forEach(function (a) {
var li = a.parentElement;
li.style.display = (!q || a.textContent.toLowerCase().indexOf(q) !== -1) ? '' : 'none';
});
});
})();
</script>
{% endblock %}