Feature: Partner-Dashboard (#partner-dashboard) — operative Daten raus aus dem Profil-Editor
Rene: QR-Stats gehören nicht ins öffentliche Profil, eigene Seite fehlte.
Neue Seite 'Partner-Bereich' (Welten-Chip 🤝 zwischen Moderation und Admin,
role:partner — sichtbar für is_partner + Admin; _mergeDefaults reicht den
Chip an bestehende Welt-Configs nach):
- Einladungscode groß + Link-kopieren-Button
- Kacheln: Registrierungen gesamt / diesen Monat / unbestätigt
- QR-Kontingente mit Einzel-Code-Status (aus partner-profil.js hierher verschoben)
- Profil-Status-Karte (Entwurf/Prüfung/frei) mit Sprung zum Editor
Backend: GET /partner/my-stats (Codes mit Zahlen + Profil-Status).
Settings-Partner-Karte: zwei Buttons (Partner-Bereich primär, Profil sekundär);
Dank-Mail-CTA zeigt auf #partner-dashboard. Suite: 52 passed.
This commit is contained in:
parent
3d7d5dc1c4
commit
0a262989f3
12 changed files with 287 additions and 96 deletions
|
|
@ -466,11 +466,11 @@ def _notify_partner_registration(user_id: int):
|
|||
+ (f"\n{qr_line}\n" if qr_line else "")
|
||||
+ f"\nDeine Bilanz mit dem Code {pc['code']}: {total} bestätigte Registrierungen insgesamt, {month} in diesem Monat.\n"
|
||||
+ (f"{founder_line}\n" if founder_line else "")
|
||||
+ f"\nDeine Statistik: {_APP_URL}/#partner-profil\n")
|
||||
+ f"\nDein Partner-Bereich: {_APP_URL}/#partner-dashboard\n")
|
||||
try:
|
||||
from routes.outreach import _send_smtp
|
||||
from mailer import email_html
|
||||
html = email_html(body_html, cta_url=f"{_APP_URL}/#partner-profil", cta_label="Meine Partner-Statistik")
|
||||
html = email_html(body_html, cta_url=f"{_APP_URL}/#partner-dashboard", cta_label="Mein Partner-Bereich")
|
||||
_send_smtp(pc["owner_email"], subject, plain, "partner", html=html)
|
||||
except Exception as exc:
|
||||
_log_smtp_failure(pc["owner_email"], subject, plain, exc, context="partner_thank_you")
|
||||
|
|
|
|||
|
|
@ -750,6 +750,37 @@ def qr_batch_pdf_admin(batch_id: int, user=Depends(require_admin)):
|
|||
headers={"Content-Disposition": f'attachment; filename="banyaro-qr-{batch_id}.pdf"'})
|
||||
|
||||
|
||||
@router.get("/partner/my-stats")
|
||||
def my_partner_stats(user=Depends(require_partner)):
|
||||
"""Dashboard-Zahlen für den Partner: eigene Codes mit Registrierungen/Versuchen
|
||||
+ Status des öffentlichen Profils."""
|
||||
with db() as conn:
|
||||
codes = conn.execute(
|
||||
"""SELECT pc.id, pc.code, pc.label, pc.uses, pc.grants_founder,
|
||||
(SELECT COUNT(*) FROM users u
|
||||
WHERE u.referred_by = -pc.id AND u.email_verified = 1) AS registrations,
|
||||
(SELECT COUNT(*) FROM users u
|
||||
WHERE u.referred_by = -pc.id AND u.email_verified = 0) AS attempts,
|
||||
(SELECT COUNT(*) FROM users u
|
||||
WHERE u.referred_by = -pc.id AND u.email_verified = 1
|
||||
AND strftime('%Y-%m', u.created_at) = strftime('%Y-%m', 'now')) AS registrations_month
|
||||
FROM partner_codes pc
|
||||
WHERE pc.owner_user_id = ?
|
||||
ORDER BY pc.created_at""",
|
||||
(user["id"],)
|
||||
).fetchall()
|
||||
profile = _pp_get_or_empty(conn, user["id"])
|
||||
return {
|
||||
"codes": [dict(c) for c in codes],
|
||||
"profile": {
|
||||
"exists": bool(profile),
|
||||
"approved": profile.get("approved", 0),
|
||||
"submitted_at": profile.get("submitted_at"),
|
||||
"display_name": profile.get("display_name"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@router.get("/partner/my-qr")
|
||||
def my_qr_batches(user=Depends(require_partner)):
|
||||
"""Übergabe/Self-Service: eigene Kontingente mit Stats (Code-Besitzer)."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue