Merge remote-tracking branch 'refs/remotes/origin/main'

This commit is contained in:
2026-09-15 16:45:31 +02:00
8 changed files with 514 additions and 95 deletions
+2 -2
View File
@@ -30,7 +30,7 @@
<p>
<strong>Registergericht:</strong> Amtsgericht Traunstein<br>
<strong>Registernummer:</strong> HRB 35441<br>
<strong>USt-ID:</strong> ####### Noch nicht vorhanden
<strong>USt-ID:</strong> ########################### Folgt noch
</p>
</section>
@@ -165,7 +165,7 @@
</section>
<div class="last-updated">
<p><strong>Zuletzt aktualisiert:</strong> 5. Mai 2026</p>
<p><strong>Zuletzt aktualisiert:</strong> 14. September 2026</p>
</div>
</div>
</div>
+10
View File
@@ -15,6 +15,16 @@
<p><strong>Subdomain:</strong> {{ instance.subdomain }}</p>
<p><strong>Status:</strong> {{ instance.status }}</p>
<p><strong>Zugang:</strong> <a href="https://{{ instance.domain }}" target="_blank" rel="noopener noreferrer">https://{{ instance.domain }}</a></p>
{% if instance.is_trial %}
<p><strong>Testversion:</strong> aktiv bis {{ instance.expires_at }}</p>
{% if instance.trial_state == "active" and not instance.upgrade_requested %}
<form method="post" action="{{ url_for('request_trial_upgrade') }}" style="margin-top: 1rem;">
<button type="submit">Auf reguläre Lizenz upgraden</button>
</form>
{% elif instance.upgrade_requested %}
<p>Ihre Upgrade-Anfrage wird vom Team bearbeitet.</p>
{% endif %}
{% endif %}
{% elif pending_request %}
<div style="background-color: #f8f9fa; border-left: 4px solid #007bff; padding: 1rem; margin-top: 1rem;">
<p><strong>Ihre Instanz wird gerade eingerichtet!</strong></p>
+62 -32
View File
@@ -146,6 +146,34 @@
</section>
<section class="packages-grid" aria-label="Buchungspakete">
<!-- TESTVERSION -->
<article class="package-card">
<div class="package-card__header">
<div>
<span class="package-card__tag" style="background: #e8f5e9; color: #2e7d32;">Kostenlos</span>
<h2 class="package-card__name">Testversion</h2>
</div>
<div class="package-card__price">
<span>0 €</span><small> -> 14 Tage</small>
</div>
</div>
<p class="package-card__description">Testen Sie alle Module (Inventar, Bibliothek & Terminplaner) 14 Tage lang unverbindlich in Ihrer Schule.</p>
<ul class="package-card__features">
<li>Voller Zugriff auf alle Basis-Module</li>
<li>Keine automatische Verlängerung</li>
<li>Vollständige Datenübernahme bei Kauf</li>
<li>Endet automatisch nach 14 Tagen</li>
</ul>
<div class="package-card__actions">
{% if 'username' in session %}
<button class="btn add-to-cart-btn" data-id="testversion" data-name="Testversion (14 Tage)" data-price="0" onclick="handleAddToCart(this)">
Jetzt kostenlos starten
</button>
{% else %}
<a class="btn secondary" href="{{ url_for('login') }}">Zum Testen einloggen</a>
{% endif %}
</div>
</article>
<!-- INVENTARSYSTEM -->
<article class="package-card">
<div class="package-card__header">
@@ -232,35 +260,6 @@
</div>
</article>
<!-- STARTPAKET (Kombi) -->
<!--<article class="package-card highlight">
<div class="package-card__header">
<div>
<span class="package-card__tag highlight-tag">Sparpaket</span>
<h2 class="package-card__name">Startpaket</h2>
</div>
<div class="package-card__price">
<span>490 €</span><small>/ Jahr</small>
</div>
</div>
<p class="package-card__description">Enthält <strong>Inventarverwaltung</strong> und <strong>Bibliothekssystem</strong> als ideale Komplettlösung zum Vorzugspreis.</p>
<ul class="package-card__features">
<li>Beinhaltet alle Features des Inventarsystems</li>
<li>Beinhaltet alle Features des Bibliothekssystems</li>
<li>Perfekter Start für Ihre Digitalisierung</li>
<li><strong>30 € Ersparnis</strong> gegenüber Einzelkauf</li>
</ul>
<div class="package-card__actions">
{% if 'username' in session %}
<button class="btn add-to-cart-btn" data-id="startpaket" data-name="Startpaket (Inventar + Bibliothek)" data-price="490" onclick="handleAddToCart(this)">
In den Warenkorb
</button>
{% else %}
<a class="btn secondary" href="{{ url_for('login') }}">Zum Buchen einloggen</a>
{% endif %}
</div>
</article>-->
<!-- SCHULTRÄGER -->
<article class="package-card">
<div class="package-card__header">
@@ -336,8 +335,8 @@
<div class="custom-modal-actions" id="cartActions">
<button class="btn secondary" type="button" onclick="closeCartModal()">Weiter umsehen</button>
<button class="btn outline" type="button" onclick="goToCheckout('consultation')">Beratungstermin anfragen</button>
<button class="btn" type="button" onclick="goToCheckout('payment')">Jetzt Zahlungspflichtig buchen</button>
<button class="btn outline" type="button" id="btnConsultation" onclick="goToCheckout('consultation')">Beratungstermin anfragen</button>
<button class="btn" type="button" id="btnCheckout" onclick="goToCheckout('payment')">Jetzt Zahlungspflichtig buchen</button>
</div>
</div>
</div>
@@ -383,7 +382,10 @@
if(item.price > 0) total += item.price;
if(item.price === 0) hasIndividualPrice = true;
const priceText = item.price > 0 ? `${item.price} €` : "Auf Anfrage";
let priceText = `${item.price} €`;
if (item.price === 0) {
priceText = item.id === 'testversion' ? 'Kostenlos' : 'Auf Anfrage';
}
container.innerHTML += `
<div class="cart-item">
@@ -404,6 +406,28 @@
} else {
totalPriceEl.innerText = `${total} € / Jahr`;
}
// Dynamic Checkout Button Text & Behavior
const btnCheckout = document.getElementById('btnCheckout');
const btnConsultation = document.getElementById('btnConsultation');
const isTrial = cart.some(item => item.id === 'testversion');
const isCustom = cart.some(item => item.id === 'schultraeger');
if (isTrial) {
btnCheckout.innerText = "Kostenlos testen (14 Tage)";
btnCheckout.setAttribute('onclick', "goToCheckout('trial')");
btnConsultation.style.display = 'none'; // Hide consultation for trials
} else if (isCustom) {
btnCheckout.innerText = "Angebot anfordern";
btnCheckout.setAttribute('onclick', "goToCheckout('consultation')"); // Force consultation
btnConsultation.style.display = 'none';
} else {
btnCheckout.innerText = "Jetzt Zahlungspflichtig buchen";
btnCheckout.setAttribute('onclick', "goToCheckout('payment')");
btnConsultation.style.display = 'inline-block';
}
}
// Artikel zum Warenkorb hinzufügen
@@ -414,6 +438,12 @@
return;
}
const hasTestversion = cart.find(i => i.id === 'testversion');
if (hasTestversion || (id === 'testversion' && cart.length > 0)) {
alert('Die Testversion beinhaltet bereits alle Module und kann nicht mit anderen Paketen kombiniert werden. Bitte leeren Sie Ihren Warenkorb zuerst.');
return;
}
// Konflikte mit Startpaket abfangen
const hasStartpaket = cart.find(i => i.id === 'startpaket');
if (hasStartpaket && (id === 'inventarsystem' || id === 'buecherei')) {
+10 -10
View File
@@ -304,6 +304,7 @@
<form id="checkout-form" method="POST" action="{{ url_for('booking_payment') }}">
<input type="hidden" name="package" value="{{ package_key }}">
<input type="hidden" name="flow" value="{{ flow }}">
<div class="booking-review__form-grid">
<div class="booking-review__section-title">1. Angaben zur Schule & Ansprechpartner</div>
@@ -524,17 +525,16 @@
let backendData = null; // Speichert das finale Backend-Resultat
let isFailed = false;
// Echte Log-Meldungen, die exakt alle 10 Sekunden durchgeschaltet werden
const statusSteps = [
{ atMs: 0, msg: "[INFO] Verarbeite Trigger: Starte Provisionierung für " + slug + "..." },
{ atMs: 10000, msg: "[INFO] Prüfe freie Ports... Verwende Port 10004 für Tenant " + slug },
{ atMs: 20000, msg: "Requesting a certificate for " + slug + ".invario-software.de..." },
{ atMs: 30000, msg: "Successfully deployed certificate to Nginx. Restarting app container..." },
{ atMs: 40000, msg: "Container inventarsystem-app-1 Recreated. Waiting for MongoDB/Redis..." },
{ atMs: 50000, msg: "Initializing database for " + slug + "... Default admin created." },
{ atMs: 60000, msg: "Module configurations updated successfully (library=on)..." },
{ atMs: 70000, msg: "Rebuilding and/or restarting app container using docker-compose..." },
{ atMs: 80000, msg: "[INFO] Bereinige verwaiste App-Container. Cleaning up old temporary files..." }
{ atMs: 0, msg: "[INFO] Processing trigger: Starting deployment for " + slug + "..." },
{ atMs: 10000, msg: "[INFO] Checking available resources and allocating network ports..." },
{ atMs: 20000, msg: "[INFO] Requesting security certificate for " + slug + "..." },
{ atMs: 30000, msg: "[INFO] Deploying certificate to proxy layer. Restarting service..." },
{ atMs: 40000, msg: "[INFO] Service containers recreated. Waiting for backend dependencies..." },
{ atMs: 50000, msg: "[INFO] Initializing storage for " + slug + "... Default admin created." },
{ atMs: 60000, msg: "[INFO] Configurations updated successfully..." },
{ atMs: 70000, msg: "[INFO] Rebuilding and restarting application stack..." },
{ atMs: 80000, msg: "[INFO] Cleaning up temporary files and orphan processes..." }
];
// 1. Hintergrund-Polling: Fragt den echten Status ab, OHNE die UI abzubrechen