Öffentliche /help-Seite — Server-rendered FAQ ohne Login
Apple-Reviewer braucht eine publik erreichbare Support-URL. Die SPA-Hilfeseite (/#hilfe) ist hinter dem Welcome-Overlay für nicht angemeldete User versteckt. Neue /help-Route rendert serverseitig: - Holt aktive FAQ-Artikel aus help_articles (über bestehendes TTL-Cache _load_active_help_articles). - Gruppiert nach Kategorie mit deutschen Labels. - Native HTML5 <details>/<summary> Akkordeon — kein JS nötig. - Dark Mode via prefers-color-scheme. - Direkter mailto support@banyaro.app + Verweis auf die volle Hilfe nach Login. Damit haben wir https://banyaro.app/help als Support-URL für App Store Connect.
This commit is contained in:
parent
d23d696745
commit
2d907f6370
6 changed files with 156 additions and 16 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
1140
|
1141
|
||||||
140
backend/main.py
140
backend/main.py
|
|
@ -1702,6 +1702,146 @@ async def presse():
|
||||||
return FileResponse(f"{STATIC_DIR}/presse.html", headers={"Cache-Control": "max-age=3600"})
|
return FileResponse(f"{STATIC_DIR}/presse.html", headers={"Cache-Control": "max-age=3600"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/help")
|
||||||
|
async def help_page():
|
||||||
|
"""Öffentliche, server-gerenderte Hilfe/FAQ-Seite. Kein Login nötig —
|
||||||
|
Apple-Reviewer und Suchmaschinen kommen hier direkt rein."""
|
||||||
|
from fastapi.responses import HTMLResponse
|
||||||
|
import html as _html
|
||||||
|
from routes.help import _load_active_help_articles
|
||||||
|
|
||||||
|
KAT_LABEL = {
|
||||||
|
"installation": "Installation & PWA",
|
||||||
|
"erste_schritte": "Erste Schritte",
|
||||||
|
"standort": "Standort & Wetter",
|
||||||
|
"account": "Account & Passwort",
|
||||||
|
"features": "Features erklärt",
|
||||||
|
"probleme": "Technische Probleme",
|
||||||
|
}
|
||||||
|
articles = _load_active_help_articles()
|
||||||
|
# Gruppieren nach Kategorie, Reihenfolge aus KAT_LABEL
|
||||||
|
by_kat: dict[str, list] = {}
|
||||||
|
for a in articles:
|
||||||
|
by_kat.setdefault(a["kategorie"], []).append(a)
|
||||||
|
kat_order = [k for k in KAT_LABEL.keys() if k in by_kat] + [
|
||||||
|
k for k in by_kat.keys() if k not in KAT_LABEL
|
||||||
|
]
|
||||||
|
|
||||||
|
sections_html = ""
|
||||||
|
for kat in kat_order:
|
||||||
|
label = KAT_LABEL.get(kat, kat.replace("_", " ").title())
|
||||||
|
items = "".join(
|
||||||
|
f'<details><summary>{_html.escape(a["frage"])}</summary>'
|
||||||
|
f'<div class="answer">{_html.escape(a["antwort"]).replace(chr(10), "<br>")}</div></details>'
|
||||||
|
for a in by_kat[kat]
|
||||||
|
)
|
||||||
|
sections_html += f'<section><h2>{_html.escape(label)}</h2>{items}</section>'
|
||||||
|
|
||||||
|
html = f"""<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Hilfe & FAQ — Ban Yaro</title>
|
||||||
|
<meta name="description" content="Antworten zu Ban Yaro und Ban Yaro Go: Installation, Standort, Account, Features.">
|
||||||
|
<link rel="icon" href="/icons/icon-180.png">
|
||||||
|
<style>
|
||||||
|
:root {{
|
||||||
|
--c-bg: #fbfaf6;
|
||||||
|
--c-text: #1c1917;
|
||||||
|
--c-text-sec: #57534e;
|
||||||
|
--c-primary: #C4843A;
|
||||||
|
--c-border: #e7e5e0;
|
||||||
|
--c-card: #fff;
|
||||||
|
}}
|
||||||
|
@media (prefers-color-scheme: dark) {{
|
||||||
|
:root {{
|
||||||
|
--c-bg: #0c0a09; --c-text: #f5f5f4; --c-text-sec: #a8a29e;
|
||||||
|
--c-border: #292524; --c-card: #1c1917;
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
* {{ box-sizing: border-box; }}
|
||||||
|
body {{
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
background: var(--c-bg); color: var(--c-text);
|
||||||
|
max-width: 760px; margin: 0 auto;
|
||||||
|
padding: 2rem 1.25rem 4rem; line-height: 1.55;
|
||||||
|
}}
|
||||||
|
a {{ color: var(--c-primary); text-decoration: none; }}
|
||||||
|
a:hover {{ text-decoration: underline; }}
|
||||||
|
h1 {{ font-size: 1.8rem; margin: 0 0 .25rem; font-weight: 800; }}
|
||||||
|
.lead {{ color: var(--c-text-sec); margin: 0 0 2rem; }}
|
||||||
|
section {{ margin-bottom: 2rem; }}
|
||||||
|
h2 {{ font-size: 1.1rem; margin: 0 0 .75rem;
|
||||||
|
color: var(--c-text); font-weight: 700;
|
||||||
|
padding-bottom: .25rem; border-bottom: 1px solid var(--c-border); }}
|
||||||
|
details {{
|
||||||
|
background: var(--c-card);
|
||||||
|
border: 1px solid var(--c-border);
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: .6rem;
|
||||||
|
padding: 0;
|
||||||
|
}}
|
||||||
|
details summary {{
|
||||||
|
cursor: pointer;
|
||||||
|
padding: .9rem 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
list-style: none;
|
||||||
|
position: relative;
|
||||||
|
padding-right: 2.5rem;
|
||||||
|
}}
|
||||||
|
details summary::-webkit-details-marker {{ display: none; }}
|
||||||
|
details summary::after {{
|
||||||
|
content: "+";
|
||||||
|
position: absolute; right: 1rem; top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-weight: 400; color: var(--c-primary);
|
||||||
|
font-size: 1.4rem; transition: transform .15s;
|
||||||
|
}}
|
||||||
|
details[open] summary::after {{ content: "−"; }}
|
||||||
|
.answer {{
|
||||||
|
padding: 0 1rem 1rem;
|
||||||
|
color: var(--c-text-sec);
|
||||||
|
}}
|
||||||
|
.contact {{
|
||||||
|
background: var(--c-card);
|
||||||
|
border: 1px solid var(--c-border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1.25rem;
|
||||||
|
margin-top: 2.5rem;
|
||||||
|
}}
|
||||||
|
.contact h2 {{ border-bottom: none; padding-bottom: 0; margin-bottom: .5rem; }}
|
||||||
|
.contact p {{ margin: .25rem 0; color: var(--c-text-sec); }}
|
||||||
|
nav.top {{ margin-bottom: 1.5rem; }}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="top"><a href="/">← banyaro.app</a></nav>
|
||||||
|
<h1>Hilfe & FAQ</h1>
|
||||||
|
<p class="lead">
|
||||||
|
Antworten zu Ban Yaro und Ban Yaro Go — der nativen iOS-App für unterwegs.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{sections_html}
|
||||||
|
|
||||||
|
<div class="contact">
|
||||||
|
<h2>Direkt-Kontakt</h2>
|
||||||
|
<p>Wenn du hier nichts findest, schreib uns:</p>
|
||||||
|
<p><a href="mailto:support@banyaro.app">support@banyaro.app</a></p>
|
||||||
|
<p style="font-size:.85rem;margin-top:1rem">
|
||||||
|
Mehr Hilfe gibt es nach dem Anmelden im Suchbereich von
|
||||||
|
<a href="/#hilfe">banyaro.app/#hilfe</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style="text-align:center;margin-top:3rem;font-size:.85rem;color:var(--c-text-sec)">
|
||||||
|
<a href="/impressum">Impressum</a> · <a href="/datenschutz">Datenschutz</a> · <a href="/agb">AGB</a>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>"""
|
||||||
|
return HTMLResponse(content=html, headers={"Cache-Control": "max-age=600"})
|
||||||
|
|
||||||
|
|
||||||
@app.get("/konto-loeschen")
|
@app.get("/konto-loeschen")
|
||||||
async def konto_loeschen():
|
async def konto_loeschen():
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
|
|
|
||||||
|
|
@ -86,14 +86,14 @@
|
||||||
<title>Ban Yaro</title>
|
<title>Ban Yaro</title>
|
||||||
|
|
||||||
<!-- Theme + theme-color Statusleiste vor CSS setzen -->
|
<!-- Theme + theme-color Statusleiste vor CSS setzen -->
|
||||||
<script src="/js/boot-early.js?v=1140"></script>
|
<script src="/js/boot-early.js?v=1141"></script>
|
||||||
|
|
||||||
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
||||||
<link rel="stylesheet" href="/css/design-system.css?v=1140">
|
<link rel="stylesheet" href="/css/design-system.css?v=1141">
|
||||||
<link rel="stylesheet" href="/css/layout.css?v=1140">
|
<link rel="stylesheet" href="/css/layout.css?v=1141">
|
||||||
<link rel="stylesheet" href="/css/components.css?v=1140">
|
<link rel="stylesheet" href="/css/components.css?v=1141">
|
||||||
<link rel="stylesheet" href="/css/utilities.css?v=1140">
|
<link rel="stylesheet" href="/css/utilities.css?v=1141">
|
||||||
<link rel="stylesheet" href="/css/lists.css?v=1140">
|
<link rel="stylesheet" href="/css/lists.css?v=1141">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
@ -617,11 +617,11 @@
|
||||||
<div id="modal-container"></div>
|
<div id="modal-container"></div>
|
||||||
|
|
||||||
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
||||||
<script src="/js/api.js?v=1140"></script>
|
<script src="/js/api.js?v=1141"></script>
|
||||||
<script src="/js/ui.js?v=1140"></script>
|
<script src="/js/ui.js?v=1141"></script>
|
||||||
<script src="/js/app.js?v=1140"></script>
|
<script src="/js/app.js?v=1141"></script>
|
||||||
<script src="/js/worlds.js?v=1140"></script>
|
<script src="/js/worlds.js?v=1141"></script>
|
||||||
<script src="/js/offline-indicator.js?v=1140"></script>
|
<script src="/js/offline-indicator.js?v=1141"></script>
|
||||||
|
|
||||||
<!-- Feature-Seiten werden lazy geladen -->
|
<!-- Feature-Seiten werden lazy geladen -->
|
||||||
|
|
||||||
|
|
@ -631,7 +631,7 @@
|
||||||
|
|
||||||
|
|
||||||
<!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) -->
|
<!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) -->
|
||||||
<script src="/js/boot.js?v=1140"></script>
|
<script src="/js/boot.js?v=1141"></script>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Router, State-Management, Navigation, Initialisierung.
|
Router, State-Management, Navigation, Initialisierung.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const APP_VER = '1140'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
const APP_VER = '1141'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||||
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
|
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
|
||||||
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
|
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
|
||||||
window.APP_VERSION = APP_VERSION;
|
window.APP_VERSION = APP_VERSION;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="color-scheme" content="light dark">
|
<meta name="color-scheme" content="light dark">
|
||||||
<script src="/js/landing-init.js?v=1140"></script>
|
<script src="/js/landing-init.js?v=1141"></script>
|
||||||
<title>Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz</title>
|
<title>Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz</title>
|
||||||
<meta name="description" content="Ban Yaro: Die kostenlose All-in-One Hunde-App für DACH. Tagebuch, Giftköder-Alarm, Training mit KI, Forum, Wurfbörse, Stammbaum, Inzucht-Check — DSGVO-konform, offline-fähig, ohne App Store.">
|
<meta name="description" content="Ban Yaro: Die kostenlose All-in-One Hunde-App für DACH. Tagebuch, Giftköder-Alarm, Training mit KI, Forum, Wurfbörse, Stammbaum, Inzucht-Check — DSGVO-konform, offline-fähig, ohne App Store.">
|
||||||
<meta name="keywords" content="Hunde App, Hunde Community, Wurfbörse, Züchter, Welpen kaufen, Stammbaum Hund, Inzuchtkoeffizient, Hundezucht, Impfpass Hund, Giftköder Alarm, Gassi Community, Hundetraining App, Hunde Forum, Hunde KI, Hundefilm Datenbank, Welpen Marktplatz">
|
<meta name="keywords" content="Hunde App, Hunde Community, Wurfbörse, Züchter, Welpen kaufen, Stammbaum Hund, Inzuchtkoeffizient, Hundezucht, Impfpass Hund, Giftköder Alarm, Gassi Community, Hundetraining App, Hunde Forum, Hunde KI, Hundefilm Datenbank, Welpen Marktplatz">
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
|
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
|
||||||
const VER = '1140';
|
const VER = '1141';
|
||||||
const CACHE_VERSION = `by-v${VER}`;
|
const CACHE_VERSION = `by-v${VER}`;
|
||||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue