Ö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
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"})
|
||||
|
||||
|
||||
@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")
|
||||
async def konto_loeschen():
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue