Changes to the generall customer pool

This commit is contained in:
2026-11-05 22:43:50 +01:00
parent 12712c4b8b
commit 4b26f1dbf5
5 changed files with 250 additions and 0 deletions
+79
View File
@@ -392,6 +392,29 @@ def _get_collection(name: str):
return client, db[name]
HOMEPAGE_MODES = {"choose", "school", "business"}
def _get_homepage_mode() -> str:
client = None
try:
client, collection = _get_collection("site_settings")
setting = collection.find_one({"key": "homepage_mode"}) or {}
mode = setting.get("value", "choose")
return mode if mode in HOMEPAGE_MODES else "choose"
except PyMongoError:
return "choose"
finally:
if client:
client.close()
def _render_audience_page(audience: str):
if audience == "business":
return render_template("business.html")
return render_template("main.html")
def _list_users_for_admin() -> list:
docs = user_store.get_all_users() or []
users = []
@@ -1846,9 +1869,37 @@ def admin_required(view_func):
@app.route("/", methods=["GET", "POST"])
def default():
mode = _get_homepage_mode()
audience = mode if mode != "choose" else session.get("homepage_audience")
if audience in {"school", "business"}:
return _render_audience_page(audience)
return render_template("audience.html")
@app.route("/audience", methods=["GET", "POST"])
def audience():
if request.method == "POST":
selected = (request.form.get("audience") or "").strip().lower()
if selected not in {"school", "business"}:
flash("Bitte wählen Sie einen gültigen Bereich aus.", "error")
return redirect(url_for("audience"))
session["homepage_audience"] = selected
return redirect(url_for("default"))
return render_template("audience.html")
@app.route("/school")
def school_homepage():
session["homepage_audience"] = "school"
return render_template("main.html")
@app.route("/business")
def business_homepage():
session["homepage_audience"] = "business"
return render_template("business.html")
@app.route('/health')
def health():
return jsonify({"status": "ok", "time": _utc_now_iso()}), 200
@@ -3529,6 +3580,34 @@ def admin_instances():
parent_domain=INSTANCE_PARENT_DOMAIN,
)
@app.route('/admin/site-settings', methods=['GET', 'POST'])
@admin_required
def admin_site_settings():
if request.method == "POST":
mode = (request.form.get("homepage_mode") or "").strip().lower()
if mode not in HOMEPAGE_MODES:
flash("Ungültige Startseiten-Einstellung.", "error")
return redirect(url_for("admin_site_settings"))
client = None
try:
client, collection = _get_collection("site_settings")
collection.update_one(
{"key": "homepage_mode"},
{"$set": {"key": "homepage_mode", "value": mode, "updated_at": _utc_now_iso()}},
upsert=True,
)
flash("Startseiten-Modus gespeichert.", "success")
except PyMongoError:
flash("Die Startseiten-Einstellung konnte nicht gespeichert werden.", "error")
finally:
if client:
client.close()
return redirect(url_for("admin_site_settings"))
return render_template("admin_site_settings.html", homepage_mode=_get_homepage_mode())
@app.route('/admin/blog', methods=['GET', 'POST'])
@admin_required
def admin_blog():
@@ -0,0 +1,28 @@
{% extends "base.html" %}
{% block title %}Admin | Startseite verwalten{% endblock %}
{% block content %}
<section style="max-width: 760px; margin: 2rem auto;">
<div style="padding: 2rem; border: 1px solid #d0d7d0; border-radius: 18px; background: #fff; box-shadow: 0 14px 30px rgba(28,45,41,.07);">
<p style="color:#1c5a63; font-weight:800; text-transform:uppercase; letter-spacing:.06em;">Admin-Einstellung</p>
<h1 style="margin:.45rem 0 .7rem;">Startseiten-Modus</h1>
<p>Steuern Sie, ob Besucher zuerst wählen oder direkt auf eine Zielgruppen-Seite gelangen.</p>
<form method="post" style="display:grid; gap:1rem; margin-top:1.5rem;">
<label style="display:flex; gap:.7rem; align-items:flex-start; padding:1rem; border:1px solid #d8e1da; border-radius:12px;">
<input type="radio" name="homepage_mode" value="choose" {% if homepage_mode == "choose" %}checked{% endif %}>
<span><strong>Auswahl anzeigen</strong><br><small>Neue Besucher wählen zwischen Schulen und Betrieben.</small></span>
</label>
<label style="display:flex; gap:.7rem; align-items:flex-start; padding:1rem; border:1px solid #d8e1da; border-radius:12px;">
<input type="radio" name="homepage_mode" value="school" {% if homepage_mode == "school" %}checked{% endif %}>
<span><strong>Schule als Standard</strong><br><small>Die bestehende Schul-Startseite wird direkt angezeigt.</small></span>
</label>
<label style="display:flex; gap:.7rem; align-items:flex-start; padding:1rem; border:1px solid #d8e1da; border-radius:12px;">
<input type="radio" name="homepage_mode" value="business" {% if homepage_mode == "business" %}checked{% endif %}>
<span><strong>Betriebe als Standard</strong><br><small>Die Business-Startseite wird direkt angezeigt.</small></span>
</label>
<button type="submit" style="justify-self:start; border:0; border-radius:999px; padding:.75rem 1.2rem; background:#1c5a63; color:#fff; font-weight:800; cursor:pointer;">Einstellung speichern</button>
</form>
</div>
</section>
{% endblock %}
+66
View File
@@ -0,0 +1,66 @@
{% extends "base.html" %}
{% set meta_title = "Invario – die passende Lösung für Ihre Organisation" %}
{% set meta_description = "Wählen Sie Invario für Schulen oder für Werkzeug- und Asset-Management in Betrieben." %}
{% block title %}Ihre Invario-Lösung{% endblock %}
{% block head %}
{{ super() }}
<style>
.audience-page { padding: clamp(1.5rem, 5vw, 4rem) 0 2rem; }
.audience-intro { max-width: 700px; margin: 0 auto 2rem; text-align: center; }
.audience-intro .eyebrow { color: #1c5a63; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; }
.audience-intro h1 { margin: .55rem 0 .8rem; color: #173d44; }
.audience-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.1rem; }
.audience-card { padding: clamp(1.3rem, 3vw, 2rem); border: 1px solid #d5ded8; border-radius: 22px; background: #fff; box-shadow: 0 18px 40px rgba(27, 55, 58, .08); }
.audience-card.business { background: linear-gradient(145deg, #102e35, #1d5e62); color: #fff; border-color: #1d5e62; }
.audience-card h2 { margin: .8rem 0 .55rem; font-size: clamp(1.35rem, 3vw, 2rem); }
.audience-card p { margin-bottom: 1.1rem; }
.business p { color: #d7edeb; }
.audience-icon { display: grid; place-items: center; width: 3rem; height: 3rem; border-radius: 14px; background: #e7f1ed; color: #1b6164; font-size: 1.45rem; }
.business .audience-icon { background: rgba(255,255,255,.14); color: #fff; }
.audience-list { display: grid; gap: .45rem; margin: 0 0 1.35rem; padding: 0; list-style: none; }
.audience-list li::before { content: "✓"; margin-right: .55rem; font-weight: 800; color: #2b8876; }
.business .audience-list li::before { color: #a7e5b7; }
.audience-button { display: inline-flex; padding: .7rem 1rem; border-radius: 999px; background: #1d6970; color: #fff; font-weight: 800; }
.business .audience-button { background: #d9f29a; color: #183d3a; }
@media (max-width: 700px) { .audience-grid { grid-template-columns: 1fr; } }
</style>
{% endblock %}
{% block content %}
<main class="audience-page">
<div class="audience-intro">
<span class="eyebrow">Willkommen bei Invario</span>
<h1>Welche Lösung passt zu Ihnen?</h1>
<p>Wählen Sie Ihren Bereich. Sie sehen anschließend Inhalte, Beispiele und Vorteile, die zu Ihrem Alltag passen.</p>
</div>
<div class="audience-grid">
<form method="post" action="{{ url_for('audience') }}" class="audience-card">
<input type="hidden" name="audience" value="school">
<div class="audience-icon" aria-hidden="true">▦</div>
<h2>Für Schulen</h2>
<p>Lehrmittel, iPads, Bücher und Ausleihe klar und sicher verwalten.</p>
<ul class="audience-list">
<li>Inventar und Medien zentral im Blick</li>
<li>Barcode-gestützte Ausgabe und Rückgabe</li>
<li>Nachvollziehbare Abläufe für Kollegium und Verwaltung</li>
</ul>
<button class="audience-button" type="submit">Schul-Lösung ansehen</button>
</form>
<form method="post" action="{{ url_for('audience') }}" class="audience-card business">
<input type="hidden" name="audience" value="business">
<div class="audience-icon" aria-hidden="true">⌁</div>
<h2>Für Betriebe</h2>
<p>Werkzeuge, Fahrzeuge und Assets im Team zuverlässig organisieren.</p>
<ul class="audience-list">
<li>Werkzeug in Sekunden finden und zuordnen</li>
<li>Transporter, Baustellen und Termine koordinieren</li>
<li>Weniger Verluste, weniger Rückfragen, mehr Überblick</li>
</ul>
<button class="audience-button" type="submit">Business-Lösung ansehen</button>
</form>
</div>
</main>
{% endblock %}
+3
View File
@@ -534,6 +534,7 @@
<a href="{{ url_for('dienstleistungen') }}">Module</a>
<a href="{{ url_for('blog') }}">Neuigkeiten</a>
<a href="{{ url_for('kontakt') }}">Kontakt</a>
<a href="{{ url_for('audience') }}">Bereich wechseln</a>
{% if session.get('is_admin') %}
<a class="nav-user-link" href="{{ url_for('user_profile') }}">Mein Profil</a>
@@ -543,6 +544,7 @@
<a class="nav-user-link" href="{{ url_for('admin_chats') }}">Admin-Chat</a>
<a class="nav-user-link" href="{{ url_for('admin_tickets') }}">Support-Center</a>
<a class="nav-user-link" href="{{ url_for('admin_instances') }}">Schul-Instanzen</a>
<a class="nav-user-link" href="{{ url_for('admin_site_settings') }}">Startseite verwalten</a>
{% elif 'username' in session %}
<a class="nav-user-link" href="{{ url_for('user_profile') }}">Mein Profil</a>
<a class="nav-user-link" href="{{ url_for('my_tutorials') }}">Tutorials</a>
@@ -569,6 +571,7 @@
<a href="{{ url_for('admin_tickets') }}">Support-Center</a>
<a href="{{ url_for('admin_blog') }}">Blog verwalten</a>
<a href="{{ url_for('admin_instances') }}">Schul-Instanzen</a>
<a href="{{ url_for('admin_site_settings') }}">Startseite verwalten</a>
</div>
</details>
<a class="nav-btn" href="{{ url_for('logout') }}">Logout</a>
+74
View File
@@ -0,0 +1,74 @@
{% extends "base.html" %}
{% set meta_title = "Werkzeug- und Asset-Management für Betriebe | Invario" %}
{% set meta_description = "Invario bringt Ordnung in Werkzeug, Fahrzeuge und Assets von Handwerks-, Elektro- und Bauunternehmen." %}
{% block title %}Werkzeugverwaltung, die mitarbeitet{% endblock %}
{% block head %}
{{ super() }}
<style>
.business-home { --ink:#102f35; --muted:#557074; --lime:#d9f29a; --teal:#1f696c; }
.business-hero { margin-top:1rem; padding:clamp(1.7rem,5vw,4.6rem); border-radius:28px; color:#fff; background: radial-gradient(circle at 90% 15%, rgba(217,242,154,.25), transparent 28%), linear-gradient(125deg,#0d2930,#1d5e62 68%,#286b67); overflow:hidden; }
.business-hero .eyebrow { color:var(--lime); font-weight:800; letter-spacing:.08em; text-transform:uppercase; }
.business-hero h1 { max-width: 760px; margin:.8rem 0 1rem; font-size:clamp(2.2rem,6vw,5rem); line-height:1.02; letter-spacing:-.04em; }
.business-hero p { max-width:650px; color:#d5e9e6; font-size:clamp(1.05rem,1.5vw,1.25rem); }
.business-actions { display:flex; flex-wrap:wrap; gap:.7rem; margin-top:1.7rem; }
.business-btn { display:inline-flex; padding:.78rem 1.15rem; border-radius:999px; background:var(--lime); color:#183c3b; font-weight:800; }
.business-btn.secondary { background:rgba(255,255,255,.1); border:1px solid rgba(255,255,255,.45); color:#fff; }
.business-section { margin-top:1.1rem; padding:clamp(1.3rem,3vw,2.2rem); border:1px solid #d5e0dc; border-radius:22px; background:#fff; }
.business-section h2 { color:var(--ink); margin-bottom:.45rem; }
.business-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:.8rem; margin-top:1.2rem; }
.business-card { padding:1.1rem; border-radius:15px; background:#f2f7f2; border:1px solid #dbe7dc; }
.business-card strong { display:block; color:var(--teal); margin-bottom:.35rem; font-size:1.08rem; }
.business-proof { display:grid; grid-template-columns:1.1fr .9fr; gap:1rem; align-items:stretch; }
.business-proof .highlight { padding:1.5rem; border-radius:18px; color:#fff; background:var(--ink); }
.business-proof .highlight p { color:#cfe1dd; }
.business-proof ul { margin:1rem 0 0; padding:0; list-style:none; display:grid; gap:.5rem; }
.business-proof li::before { content:"✓"; color:var(--lime); font-weight:800; margin-right:.5rem; }
.business-cta { display:flex; justify-content:space-between; align-items:center; gap:1rem; background:#e9f1e7; }
@media (max-width:780px) { .business-grid,.business-proof { grid-template-columns:1fr; } .business-cta { align-items:flex-start; flex-direction:column; } }
</style>
{% endblock %}
{% block content %}
<main class="business-home">
<header class="business-hero">
<span class="eyebrow">Für Handwerk, Elektro &amp; Bau</span>
<h1>Werkzeug finden. Auftrag starten. Feierabend machen.</h1>
<p>Invario macht aus verstreuten Werkzeugkisten, Transportern und Excel-Listen einen klaren digitalen Bestand. Damit Ihr Team weiß, was wo ist – und wer es zuletzt hatte.</p>
<div class="business-actions">
<a class="business-btn" href="{{ url_for('kontakt') }}">Persönliche Demo anfragen</a>
<a class="business-btn secondary" href="#module">Module entdecken</a>
</div>
</header>
<section class="business-section" id="module">
<h2>Die tägliche Unordnung verschwindet</h2>
<p>Die bestehende Invario-Modul-Suite ist für den Betrieb gedacht: schnell erfassbar, mobil nutzbar und ohne langes Einführungsprojekt.</p>
<div class="business-grid">
<article class="business-card"><strong>Inventar &amp; Assets</strong><p>Werkzeuge, Maschinen, Verbrauchsmaterial und Schutzausrüstung mit Status, Standort und Verantwortlichkeit.</p></article>
<article class="business-card"><strong>Barcode-Scanner</strong><p>Ausgabe und Rücknahme direkt am Fahrzeug oder auf der Baustelle. Jeder Vorgang bleibt nachvollziehbar.</p></article>
<article class="business-card"><strong>Termine &amp; Objekte</strong><p>Aufträge, Baustellen, Fahrzeuge und Material zusammenführen – damit die Vorbereitung nicht im Chat verloren geht.</p></article>
</div>
</section>
<section class="business-section business-proof">
<div>
<h2>Für Inhaber gemacht, die entscheiden</h2>
<p>Kurze Vertriebs- und Einführungszyklen statt monatelanger Softwareprojekte. Wir zeigen Ihnen am echten Bestand, wie Invario in Ihren Ablauf passt.</p>
<ul>
<li>Weniger Suchzeit und weniger Ersatzkäufe</li>
<li>Klare Verantwortlichkeiten im Team</li>
<li>Schlanke Fixkosten, die sich schnell rechnen</li>
</ul>
</div>
<aside class="highlight"><h3>Einfach starten</h3><p>Bestand aufnehmen, QR- oder Barcodes vergeben, Team einladen – und sofort den ersten Auftrag digital vorbereiten.</p></aside>
</section>
<section class="business-section business-cta">
<div><h2>Sehen Sie, was in Ihrem Betrieb möglich ist.</h2><p>Ein kurzes Gespräch genügt für eine konkrete Einschätzung.</p></div>
<a class="business-btn" href="{{ url_for('kontakt') }}">Gespräch vereinbaren</a>
</section>
</main>
{% endblock %}