diff --git a/VERSION b/VERSION
index d3e6945..a01282d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1269
\ No newline at end of file
+1270
\ No newline at end of file
diff --git a/backend/routes/breeder.py b/backend/routes/breeder.py
index 8279229..457766e 100644
--- a/backend/routes/breeder.py
+++ b/backend/routes/breeder.py
@@ -402,7 +402,8 @@ async def breeder_public_profile(zwingername: str):
# Sichtbare Würfe
wuerfe = conn.execute("""
- SELECT id, vater_name, mutter_name, geburt_datum, erwartetes_datum,
+ SELECT id, wurf_rang, wurf_name, vater_name, mutter_name,
+ geburt_datum, erwartetes_datum,
status, welpen_gesamt, welpen_verfuegbar, preis_spanne, beschreibung
FROM litters
WHERE breeder_id=? AND sichtbar=1 AND status != 'abgeschlossen'
@@ -410,6 +411,19 @@ async def breeder_public_profile(zwingername: str):
""", (breeder_id,)).fetchall()
result["wuerfe"] = [dict(w) for w in wuerfe]
+ # Mitgliedschaften & Zertifikate (öffentliche Logos/Badges mit Caption)
+ certs = conn.execute("""
+ SELECT id, file_path, thumbnail_path, caption FROM breeder_photos
+ WHERE breeder_id=? AND entity_type='certificate' AND visibility='public'
+ ORDER BY sort_order
+ """, (breeder_id,)).fetchall()
+ result["zertifikate"] = [{
+ "id": c["id"],
+ "url": f"/media/{c['file_path']}",
+ "thumbnail_url": f"/media/{c['thumbnail_path']}" if c["thumbnail_path"] else f"/media/{c['file_path']}",
+ "caption": c["caption"],
+ } for c in certs]
+
# Gesundheits-Statistik (aggregiert über alle öffentlichen Hunde)
hd_stats = conn.execute("""
SELECT ergebnis, COUNT(*) as cnt FROM dog_health_tests
@@ -496,6 +510,7 @@ async def breeder_my_editor(user=Depends(require_breeder)):
"""Daten für den Profil-Editor: Profil + eigene Würfe + Speicherverbrauch.
(Frontend breeder-editor.js stammt aus 459cd42 — dieser Lese-Endpoint
ging damals im Worktree-Merge verloren, wie /partner/my-profile.)"""
+ from routes.breeder_photos import _photo_dict
with db() as conn:
profile = conn.execute(
"SELECT * FROM breeder_profiles WHERE user_id=?", (user["id"],)
@@ -503,8 +518,20 @@ async def breeder_my_editor(user=Depends(require_breeder)):
if not profile:
raise HTTPException(404, "Noch kein Züchter-Profil angelegt.")
profile = dict(profile)
+ profile["photos"] = [_photo_dict(r) for r in conn.execute(
+ "SELECT * FROM breeder_photos WHERE breeder_id=? AND entity_type='breeder' ORDER BY sort_order",
+ (profile["id"],)
+ ).fetchall()]
+ # Mitgliedschaften & Zertifikate (Logos/Badges fürs öffentliche Profil)
+ profile["certificates"] = [_photo_dict(r) for r in conn.execute(
+ "SELECT * FROM breeder_photos WHERE breeder_id=? AND entity_type='certificate' ORDER BY sort_order",
+ (profile["id"],)
+ ).fetchall()]
litters = [dict(r) for r in conn.execute(
- "SELECT * FROM litters WHERE breeder_id=? ORDER BY created_at DESC",
+ """SELECT l.*,
+ (SELECT COUNT(*) FROM breeder_photos p
+ WHERE p.entity_type='litter' AND p.entity_id=l.id) AS foto_count
+ FROM litters l WHERE l.breeder_id=? ORDER BY l.created_at DESC""",
(profile["id"],)
).fetchall()]
diff --git a/backend/routes/breeder_photos.py b/backend/routes/breeder_photos.py
index 802440f..a080e9b 100644
--- a/backend/routes/breeder_photos.py
+++ b/backend/routes/breeder_photos.py
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")
-_VALID_ENTITY_TYPES = {"breeder", "litter", "puppy", "parent"}
+_VALID_ENTITY_TYPES = {"breeder", "litter", "puppy", "parent", "certificate"}
# ------------------------------------------------------------------
@@ -100,7 +100,7 @@ async def upload_photo(
elif entity_type == "parent":
# parent kann frei hochgeladen werden solange breeder stimmt
pass
- elif entity_type == "breeder":
+ elif entity_type in ("breeder", "certificate"):
# entity_id muss das eigene Profil sein
if entity_id != breeder_id and user["rolle"] != "admin":
raise HTTPException(403, "Kein Zugriff auf dieses Züchter-Profil.")
@@ -200,7 +200,7 @@ async def get_photos(
).fetchone()
if bp:
# Besitzer wenn entity dem Züchter gehört
- if entity_type == "breeder":
+ if entity_type in ("breeder", "certificate"):
is_owner = (bp["id"] == entity_id)
elif entity_type == "litter":
row = conn.execute(
diff --git a/backend/static/index.html b/backend/static/index.html
index 8c68db2..bedfe18 100644
--- a/backend/static/index.html
+++ b/backend/static/index.html
@@ -86,14 +86,14 @@
Ban Yaro
-
+
-
-
-
-
-
+
+
+
+
+
@@ -620,11 +620,11 @@
-
-
-
-
-
+
+
+
+
+
@@ -634,7 +634,7 @@
-
+
diff --git a/backend/static/js/app.js b/backend/static/js/app.js
index 3820110..5bfdfc1 100644
--- a/backend/static/js/app.js
+++ b/backend/static/js/app.js
@@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
-const APP_VER = '1269'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
+const APP_VER = '1270'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
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_VERSION = APP_VERSION;
diff --git a/backend/static/js/pages/breeder-editor.js b/backend/static/js/pages/breeder-editor.js
index eec5525..0da8054 100644
--- a/backend/static/js/pages/breeder-editor.js
+++ b/backend/static/js/pages/breeder-editor.js
@@ -138,6 +138,34 @@ window.Page_breeder_editor = (() => {
+
+
+
+ Mitgliedschaften & Zertifikate
+
+
+ Vereins-Logos, VDH-Mitgliedschaft, Urkunden — werden auf deiner öffentlichen
+ Profilseite in einer eigenen Sektion gezeigt.
+