feat: Add print functionality and improve layout for various templates
This commit is contained in:
@@ -3,9 +3,14 @@
|
||||
{% block title %}Admin | Rechnungsverwaltung{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="panel">
|
||||
<h1>Rechnungsverwaltung</h1>
|
||||
<p>Rechnungen für Nutzer erstellen, aktualisieren und löschen. Neue Rechnungen starten mit dem Status „Zu prüfen“, damit der Admin sie zuerst kontrolliert. Direktbuchungen werden hier automatisch mit zugewiesenem Admin und Buchungsunterlagen angezeigt.</p>
|
||||
<section class="panel" style="display:flex; align-items:center; justify-content:space-between; gap:1rem;">
|
||||
<div>
|
||||
<h1>Rechnungsverwaltung</h1>
|
||||
<p style="margin:0">Rechnungen für Nutzer erstellen, aktualisieren und löschen. Neue Rechnungen starten mit dem Status „Zu prüfen“, damit der Admin sie zuerst kontrolliert. Direktbuchungen werden hier automatisch mit zugewiesenem Admin und Buchungsunterlagen angezeigt.</p>
|
||||
</div>
|
||||
<div class="no-print">
|
||||
<button class="print-btn" type="button" onclick="printDocument()">Seite drucken</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="layout">
|
||||
@@ -39,9 +44,11 @@
|
||||
<p><strong>Unterlagen:</strong> {{ item.paperwork_status or 'Offen' }}{% if item.consent_accepted %} | Einwilligung bestätigt{% endif %}</p>
|
||||
<p><strong>Kontaktdaten:</strong> {{ item.booking_data.contact_person }} | {{ item.booking_data.contact_email }} | {{ item.booking_data.billing_city }}</p>
|
||||
{% endif %}
|
||||
{% if item.pdf_path %}
|
||||
<p><strong>PDF:</strong> <a href="{{ url_for('static', filename=item.pdf_path) }}" target="_blank" rel="noopener">Ansehen</a></p>
|
||||
{% endif %}
|
||||
{% if item.pdf_path %}
|
||||
<p><strong>PDF:</strong> <a href="{{ url_for('static', filename=item.pdf_path) }}" target="_blank" rel="noopener">Ansehen</a>
|
||||
<button class="print-btn" type="button" onclick="printPdf('{{ url_for('static', filename=item.pdf_path) }}')">PDF drucken</button>
|
||||
</p>
|
||||
{% endif %}
|
||||
<form method="post" class="inline-form" enctype="multipart/form-data">
|
||||
<input type="hidden" name="action" value="update">
|
||||
<input type="hidden" name="invoice_id" value="{{ item.id }}">
|
||||
|
||||
@@ -682,6 +682,29 @@
|
||||
}
|
||||
</style>
|
||||
</footer>
|
||||
<style>
|
||||
/* Print button shared style */
|
||||
.print-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.45rem 0.75rem;
|
||||
background: #ffffff;
|
||||
border: 1px solid #ccd5cc;
|
||||
border-radius: 8px;
|
||||
color: #184f57;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
.print-btn.primary {
|
||||
background: linear-gradient(120deg, #286f78 0%, #194f56 100%);
|
||||
color: white;
|
||||
border-color: #184f57;
|
||||
}
|
||||
@media print {
|
||||
.no-print { display: none !important; }
|
||||
}
|
||||
</style>
|
||||
<div class="cookie-banner hidden" id="cookieBanner" role="dialog" aria-live="polite" aria-label="Cookie Hinweis">
|
||||
<p>Wir verwenden Cookies, um die Website sicher und benutzerfreundlich zu betreiben. Du kannst auswählen, welche Cookies du erlaubst. Details in <a href="{{ url_for('datenschutz') }}">Datenschutz</a>.</p>
|
||||
<div class="cookie-actions">
|
||||
@@ -899,6 +922,38 @@
|
||||
|
||||
// Run once on load in case user already saved consent
|
||||
applyConsent();
|
||||
// Global print helpers
|
||||
window.printDocument = function () {
|
||||
try { window.print(); } catch (e) { console.error('print error', e); }
|
||||
};
|
||||
|
||||
window.printPdf = function (url) {
|
||||
try {
|
||||
var w = window.open(url, '_blank');
|
||||
if (!w) return;
|
||||
// Attempt to print after load; poll for readyState
|
||||
var attempts = 0;
|
||||
var t = setInterval(function () {
|
||||
attempts++;
|
||||
try {
|
||||
if (w.document && (w.document.readyState === 'complete' || w.document.readyState === 'interactive')) {
|
||||
w.focus();
|
||||
w.print();
|
||||
clearInterval(t);
|
||||
}
|
||||
} catch (err) {
|
||||
// cross-domain or plugin viewer may block access; fallback: focus window and leave to user
|
||||
if (attempts > 8) {
|
||||
clearInterval(t);
|
||||
try { w.focus(); } catch (e) {}
|
||||
}
|
||||
}
|
||||
if (attempts > 30) clearInterval(t);
|
||||
}, 300);
|
||||
} catch (e) {
|
||||
console.error('printPdf error', e);
|
||||
}
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -4,9 +4,14 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="legal-page">
|
||||
<div class="legal-header">
|
||||
<h1>Datenschutzerklärung</h1>
|
||||
<p>Gültig ab: 15. Mai 2026</p>
|
||||
<div class="legal-header" style="display:flex; align-items:flex-start; justify-content:space-between; gap:1rem;">
|
||||
<div>
|
||||
<h1>Datenschutzerklärung</h1>
|
||||
<p>Gültig ab: 15. Mai 2026</p>
|
||||
</div>
|
||||
<div class="no-print">
|
||||
<button class="print-btn" type="button" onclick="printDocument()">Dokument drucken</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="legal-content">
|
||||
|
||||
@@ -98,47 +98,108 @@
|
||||
|
||||
{% block content %}
|
||||
<section class="page-hero">
|
||||
<h1>Leistungen für Bildungseinrichtungen</h1>
|
||||
<p>Invario bündelt die Leistungen so, dass Schulen sofort verstehen, was sie bekommen: Inventarverwaltung für 250 € pro Jahr, Bibliothekssystem für 100 € pro Jahr für Schulen unter 400 Schülern und 150 € pro Jahr für größere Schulen, dazu Ticket-Support, Detailierte Video Schulung und dokumentierte Sicherheit.</p>
|
||||
<div class="bento" aria-label="Kernleistungen">
|
||||
<div class="bento-item"><strong>Schulen</strong>5-Minuten-Start, Zettelwirtschaft vermeiden, intuitive Web-App ohne Installation.</div>
|
||||
<div class="bento-item"><strong>Schulträger</strong>Rechtssicherheit, revisionssichere PDF-Berichte und Serverstandort Deutschland.</div>
|
||||
<h1>Module & Leitfäden</h1>
|
||||
<p>Diese Seite stellt vier austauschbare Module dar. Jedes Modul ist als kompakter, wiki‑ähnlicher Leitfaden gehalten — übersichtlich, schnell zu scannen und leicht editierbar.</p>
|
||||
</section>
|
||||
|
||||
<section class="module-shell" aria-label="Module Übersicht">
|
||||
<div class="module-nav" role="navigation" aria-label="Module Navigation">
|
||||
<ul>
|
||||
<li class="active" data-module="mod1"><a href="#mod1">Modul 1</a></li>
|
||||
<li data-module="mod2"><a href="#mod2">Modul 2</a></li>
|
||||
<li data-module="mod3"><a href="#mod3">Modul 3</a></li>
|
||||
<li data-module="mod4"><a href="#mod4">Modul 4</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="module-content" id="moduleContent">
|
||||
<article id="mod1" class="module-panel">
|
||||
<h2>Modul 1 — Titel</h2>
|
||||
<p class="lede">Kurze Zusammenfassung des Moduls. (Platzhalter — du ersetzt den Inhalt später.)</p>
|
||||
<h3>Was ist das?</h3>
|
||||
<p>Beschreibe hier den Zweck, Zielgruppe und schnelle Anwendungsfälle.</p>
|
||||
<h3>Schnellstart</h3>
|
||||
<ol>
|
||||
<li>Schritt 1 — Kurzer Tipp.</li>
|
||||
<li>Schritt 2 — Konfigurationsempfehlung.</li>
|
||||
<li>Schritt 3 — Prüfpunkte.</li>
|
||||
</ol>
|
||||
<h3>FAQs</h3>
|
||||
<dl>
|
||||
<dt>Frage: Wie mache ich X?</dt>
|
||||
<dd>Antwort: Kurze Anleitung.</dd>
|
||||
</dl>
|
||||
</article>
|
||||
|
||||
<article id="mod2" class="module-panel hidden">
|
||||
<h2>Modul 2 — Titel</h2>
|
||||
<p class="lede">Platzhaltertext für Modul 2.</p>
|
||||
</article>
|
||||
|
||||
<article id="mod3" class="module-panel hidden">
|
||||
<h2>Modul 3 — Titel</h2>
|
||||
<p class="lede">Platzhaltertext für Modul 3.</p>
|
||||
</article>
|
||||
|
||||
<article id="mod4" class="module-panel hidden">
|
||||
<h2>Modul 4 — Titel</h2>
|
||||
<p class="lede">Platzhaltertext für Modul 4.</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="service-grid">
|
||||
<article class="service-card">
|
||||
<h2>Digitale Inventarverwaltung</h2>
|
||||
<p>Alle Geräte, Räume und Zuständigkeiten an einem Ort. Das verkürzt Suchzeiten und reduziert Rückfragen.</p>
|
||||
</article>
|
||||
<article class="service-card">
|
||||
<h2>Instanz- und Fristenkontrolle</h2>
|
||||
<p>Instanzzustände und wichtige Wartungsfristen werden transparent angezeigt, inklusive klarer Hinweise für den Betrieb.</p>
|
||||
</article>
|
||||
<article class="service-card">
|
||||
<h2>Support mit klaren Prioritäten</h2>
|
||||
<p>Störungen werden strukturiert aufgenommen, priorisiert und mit nachvollziehbarem Status bearbeitet.</p>
|
||||
</article>
|
||||
<article class="service-card">
|
||||
<h2>DSGVO-konforme Dokumentation</h2>
|
||||
<p>Vorgänge und Änderungen bleiben nachvollziehbar dokumentiert, damit Prüfungen und Abstimmungen einfacher werden.</p>
|
||||
</article>
|
||||
<article class="service-card">
|
||||
<h2>Einrichtung und Schulung</h2>
|
||||
<p>Die Einführung erfolgt in klaren, alltagsnahen Schritten, damit der laufende Betrieb stabil bleibt.</p>
|
||||
</article>
|
||||
<article class="service-card">
|
||||
<h2>Begleitung im laufenden Betrieb</h2>
|
||||
<p>Feste Ansprechpartner unterstützen bei Fragen, Erweiterungen und organisatorischen Anpassungen.</p>
|
||||
</article>
|
||||
<article class="service-card">
|
||||
<h2>Technische Sicherheitsbasis</h2>
|
||||
<p>Rollenrechte, sichere Sitzungen, Schutz-Header und protokollierte Änderungen bilden die Grundlage für einen verlässlichen Betrieb.</p>
|
||||
</article>
|
||||
</section>
|
||||
<style>
|
||||
/* Module page styles */
|
||||
.module-shell { display: grid; grid-template-columns: 260px 1fr; gap: 1rem; margin-top: 1rem; }
|
||||
.module-nav { border: 1px solid #d8dfd7; border-radius: 10px; background: #fff; padding: 0.6rem; }
|
||||
.module-nav ul { list-style: none; margin: 0; padding: 0; }
|
||||
.module-nav li { margin: 0.25rem 0; }
|
||||
.module-nav a { display: block; padding: 0.6rem 0.8rem; color: #173f46; text-decoration: none; border-radius: 8px; }
|
||||
.module-nav li.active a { background: linear-gradient(90deg,#eaf6f4,#f3fbf8); font-weight:700; }
|
||||
.module-content { border: 1px solid #d8dfd7; border-radius: 12px; padding: 1.1rem; background: #fff; }
|
||||
.module-panel { max-width: 80ch; }
|
||||
.module-panel.hidden { display: none; }
|
||||
.module-panel h2 { margin-top: 0; color: #133e40; }
|
||||
.module-panel .lede { color: #455e5e; font-weight: 600; }
|
||||
.module-panel h3 { margin-top: 1rem; color: #22595b; }
|
||||
.module-panel ol { margin-left: 1.1rem; }
|
||||
|
||||
<section class="highlight">
|
||||
<h2>Besonders geeignet für Teams mit wenig Zeit</h2>
|
||||
<p>Die Oberfläche ist bewusst reduziert aufgebaut: große, klare Bedienelemente, eindeutige Bezeichnungen und kurze Klickwege für tägliche Verwaltungsaufgaben.</p>
|
||||
</section>
|
||||
@media (max-width: 900px) {
|
||||
.module-shell { grid-template-columns: 1fr; }
|
||||
.module-nav { order: 2; }
|
||||
.module-content { order: 1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Simple client-side module switching for an immediate wiki-like experience
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var navItems = document.querySelectorAll('.module-nav li');
|
||||
navItems.forEach(function (li) {
|
||||
li.addEventListener('click', function (ev) {
|
||||
ev.preventDefault();
|
||||
var target = li.getAttribute('data-module');
|
||||
if (!target) return;
|
||||
// activate nav
|
||||
navItems.forEach(function (n) { n.classList.remove('active'); });
|
||||
li.classList.add('active');
|
||||
// show panel
|
||||
document.querySelectorAll('.module-panel').forEach(function (p) { p.classList.add('hidden'); });
|
||||
var panel = document.getElementById(target);
|
||||
if (panel) panel.classList.remove('hidden');
|
||||
// update hash
|
||||
history.replaceState(null, '', '#'+target);
|
||||
// scroll into view on small screens
|
||||
if (window.innerWidth < 900) panel.scrollIntoView({ behavior: 'smooth' });
|
||||
});
|
||||
});
|
||||
|
||||
// If hash present on load, open that module
|
||||
var hash = window.location.hash.replace('#','');
|
||||
if (hash) {
|
||||
var initial = document.querySelector('.module-nav li[data-module="'+hash+'"]');
|
||||
if (initial) initial.click();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -4,9 +4,14 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="legal-page">
|
||||
<div class="legal-header">
|
||||
<h1>Impressum</h1>
|
||||
<p>Angaben gemäß TMG (Telemediengesetz) und MDStV (Mediendienstestaatsvertrag)</p>
|
||||
<div class="legal-header" style="display:flex; align-items:flex-start; justify-content:space-between; gap:1rem;">
|
||||
<div>
|
||||
<h1>Impressum</h1>
|
||||
<p>Angaben gemäß TMG (Telemediengesetz) und MDStV (Mediendienstestaatsvertrag)</p>
|
||||
</div>
|
||||
<div class="no-print">
|
||||
<button class="print-btn" type="button" onclick="printDocument()">Dokument drucken</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="legal-content">
|
||||
|
||||
@@ -3,9 +3,14 @@
|
||||
{% block title %}Meine Rechnungen{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="panel">
|
||||
<h1>Rechnungseinsicht</h1>
|
||||
<p>Alle Rechnungen im Überblick. Der Admin prüft neue Rechnungen zuerst; der Status zeigt dir, ob die Rechnung noch geprüft wird oder bereits bearbeitet wurde.</p>
|
||||
<section class="panel" style="display:flex; align-items:center; justify-content:space-between; gap:1rem;">
|
||||
<div>
|
||||
<h1>Rechnungseinsicht</h1>
|
||||
<p style="margin:0">Alle Rechnungen im Überblick. Der Admin prüft neue Rechnungen zuerst; der Status zeigt dir, ob die Rechnung noch geprüft wird oder bereits bearbeitet wurde.</p>
|
||||
</div>
|
||||
<div class="no-print">
|
||||
<button class="print-btn" type="button" onclick="printDocument()">Seite drucken</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="table-wrap">
|
||||
@@ -30,9 +35,10 @@
|
||||
<td>{{ invoice.status }}</td>
|
||||
<td>
|
||||
{% if invoice.pdf_path %}
|
||||
<a href="{{ url_for('static', filename=invoice.pdf_path) }}" target="_blank" rel="noopener">PDF ansehen</a>
|
||||
<a href="{{ url_for('static', filename=invoice.pdf_path) }}" target="_blank" rel="noopener">PDF ansehen</a>
|
||||
<button class="print-btn" type="button" onclick="printPdf('{{ url_for('static', filename=invoice.pdf_path) }}')">PDF drucken</button>
|
||||
{% else %}
|
||||
-
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -3,9 +3,14 @@
|
||||
{% block title %}Nutzungsbedingungen | Invario{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>Allgemeine Nutzungs- und Lizenzbedingungen für die Invario UG Modul Suite</h1>
|
||||
<p><strong>Stand:</strong> Mai 2026</p>
|
||||
<article class="panel" style="display:flex; align-items:flex-start; justify-content:space-between; gap:1rem;">
|
||||
<div>
|
||||
<h1>Allgemeine Nutzungs- und Lizenzbedingungen für die Invario UG Modul Suite</h1>
|
||||
<p><strong>Stand:</strong> Mai 2026</p>
|
||||
</div>
|
||||
<div class="no-print">
|
||||
<button class="print-btn" type="button" onclick="printDocument()">Dokument drucken</button>
|
||||
</div>
|
||||
|
||||
<h2>Inhaltsverzeichnis</h2>
|
||||
<ol>
|
||||
|
||||
Reference in New Issue
Block a user