Partner-Freigabe: Live-Vorschau im Admin + Mail-Fehler sichtbar machen
Rene: 'kann nichts prüfen — ich würde den Output erwarten, der auf der Partner-Seite zu sehen sein wird'. Freigabe-Zeile hat jetzt einen Vorschau-Toggle, der die Karte 1:1 wie auf #partner rendert (Logo/Initial, Slogan, Website, Instagram, Bio, Medien-Grid). Mail-Ursache gefunden: Staging-.env fehlte SMTP_SUPPORT_USER → Code-Default support@banyaro.de → 535 Auth-Fehler, vom Silent-Catch verschluckt. .env ergänzt (partner@banyaro.app wie Prod); Submit loggt SMTP-Fehler jetzt über _log_smtp_failure in failed_emails statt still zu schlucken.
This commit is contained in:
parent
a40aa183ec
commit
cadfb24a8d
7 changed files with 84 additions and 42 deletions
|
|
@ -481,20 +481,18 @@ def submit_partner_profile(user=Depends(require_partner)):
|
|||
(user["id"],)
|
||||
)
|
||||
profile = _pp_get_or_empty(conn, user["id"])
|
||||
# Admin benachrichtigen (best effort — Silent-Skip ohne ADMIN_EMAIL)
|
||||
# Admin benachrichtigen — Fehler landen in failed_emails (Admin-Retry), kein Silent-Skip
|
||||
admin_email = os.getenv("ADMIN_EMAIL", "")
|
||||
if admin_email and profile.get("approved") != 1:
|
||||
subject = f"[Ban Yaro] Partner-Profil eingereicht: {profile.get('display_name')}"
|
||||
body = (f"Partner {user['name']} ({user['email']}) hat sein Profil zur "
|
||||
f"Freigabe eingereicht.\n\nAdmin-Panel: https://banyaro.app/#admin")
|
||||
try:
|
||||
from routes.outreach import _send_smtp
|
||||
_send_smtp(
|
||||
admin_email,
|
||||
f"[Ban Yaro] Partner-Profil eingereicht: {profile.get('display_name')}",
|
||||
(f"Partner {user['name']} ({user['email']}) hat sein Profil zur "
|
||||
f"Freigabe eingereicht.\n\nAdmin-Panel: https://banyaro.app/#admin"),
|
||||
"support",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
_send_smtp(admin_email, subject, body, "support")
|
||||
except Exception as exc:
|
||||
from routes.auth import _log_smtp_failure
|
||||
_log_smtp_failure(admin_email, subject, body, exc, context="partner_profile_submit")
|
||||
return {"profile": profile}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -86,14 +86,14 @@
|
|||
<title>Ban Yaro</title>
|
||||
|
||||
<!-- Theme + theme-color Statusleiste vor CSS setzen -->
|
||||
<script src="/js/boot-early.js?v=1255"></script>
|
||||
<script src="/js/boot-early.js?v=1256"></script>
|
||||
|
||||
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
||||
<link rel="stylesheet" href="/css/design-system.css?v=1255">
|
||||
<link rel="stylesheet" href="/css/layout.css?v=1255">
|
||||
<link rel="stylesheet" href="/css/components.css?v=1255">
|
||||
<link rel="stylesheet" href="/css/utilities.css?v=1255">
|
||||
<link rel="stylesheet" href="/css/lists.css?v=1255">
|
||||
<link rel="stylesheet" href="/css/design-system.css?v=1256">
|
||||
<link rel="stylesheet" href="/css/layout.css?v=1256">
|
||||
<link rel="stylesheet" href="/css/components.css?v=1256">
|
||||
<link rel="stylesheet" href="/css/utilities.css?v=1256">
|
||||
<link rel="stylesheet" href="/css/lists.css?v=1256">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
@ -612,11 +612,11 @@
|
|||
<div id="modal-container"></div>
|
||||
|
||||
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
||||
<script src="/js/api.js?v=1255"></script>
|
||||
<script src="/js/ui.js?v=1255"></script>
|
||||
<script src="/js/app.js?v=1255"></script>
|
||||
<script src="/js/worlds.js?v=1255"></script>
|
||||
<script src="/js/offline-indicator.js?v=1255"></script>
|
||||
<script src="/js/api.js?v=1256"></script>
|
||||
<script src="/js/ui.js?v=1256"></script>
|
||||
<script src="/js/app.js?v=1256"></script>
|
||||
<script src="/js/worlds.js?v=1256"></script>
|
||||
<script src="/js/offline-indicator.js?v=1256"></script>
|
||||
|
||||
<!-- Feature-Seiten werden lazy geladen -->
|
||||
|
||||
|
|
@ -626,7 +626,7 @@
|
|||
|
||||
|
||||
<!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) -->
|
||||
<script src="/js/boot.js?v=1255"></script>
|
||||
<script src="/js/boot.js?v=1256"></script>
|
||||
|
||||
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '1255'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '1256'; // ← 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;
|
||||
|
|
|
|||
|
|
@ -2395,23 +2395,60 @@ window.Page_admin = (() => {
|
|||
${profiles.length === 0
|
||||
? `<p class="text-sm-muted">Noch keine Partner-Profile angelegt.</p>`
|
||||
: profiles.map(p => `
|
||||
<div style="display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) 0;border-bottom:1px solid var(--c-border)" data-pp-uid="${p.user_id}">
|
||||
${p.logo_url
|
||||
? `<img src="${UI.escape(p.logo_url)}" style="width:36px;height:36px;border-radius:var(--radius-md);object-fit:contain;background:var(--c-surface-2);flex-shrink:0">`
|
||||
: `<div style="width:36px;height:36px;border-radius:var(--radius-md);background:var(--c-surface-2);flex-shrink:0"></div>`}
|
||||
<div class="flex-1-min">
|
||||
<div style="font-weight:600;font-size:var(--text-sm)">${UI.escape(p.display_name || p.name)}</div>
|
||||
<div class="text-xs-muted">${UI.escape(p.name)} · ${UI.escape(p.email)}${p.photos?.length ? ` · ${p.photos.length} Medien` : ''}</div>
|
||||
<div style="border-bottom:1px solid var(--c-border)" data-pp-uid="${p.user_id}">
|
||||
<div style="display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) 0">
|
||||
${p.logo_url
|
||||
? `<img src="${UI.escape(p.logo_url)}" style="width:36px;height:36px;border-radius:var(--radius-md);object-fit:contain;background:var(--c-surface-2);flex-shrink:0">`
|
||||
: `<div style="width:36px;height:36px;border-radius:var(--radius-md);background:var(--c-surface-2);flex-shrink:0"></div>`}
|
||||
<div class="flex-1-min">
|
||||
<div style="font-weight:600;font-size:var(--text-sm)">${UI.escape(p.display_name || p.name)}</div>
|
||||
<div class="text-xs-muted">${UI.escape(p.name)} · ${UI.escape(p.email)}${p.photos?.length ? ` · ${p.photos.length} Medien` : ''}</div>
|
||||
</div>
|
||||
${p.approved === 1
|
||||
? `<span class="badge" style="background:#dcfce7;color:#16a34a">✓ Frei</span>`
|
||||
: p.approved === -1
|
||||
? `<span class="badge" style="background:#fee2e2;color:#dc2626">✗ Abgelehnt</span>`
|
||||
: p.submitted_at
|
||||
? `<span class="badge" style="background:#fef9c3;color:#a16207">⏳ Prüfen</span>`
|
||||
: `<span class="badge">Entwurf</span>`}
|
||||
<button class="btn btn-sm btn-secondary adm-pp-preview" data-uid="${p.user_id}">
|
||||
${UI.icon('eye')} Vorschau
|
||||
</button>
|
||||
${p.approved !== 1 ? `<button class="btn btn-sm btn-primary adm-pp-review" data-uid="${p.user_id}" data-val="1">✓ Freigeben</button>` : ''}
|
||||
${p.approved !== -1 ? `<button class="btn btn-sm btn-ghost adm-pp-review text-danger" data-uid="${p.user_id}" data-val="-1">✗</button>` : ''}
|
||||
</div>
|
||||
<!-- Vorschau: so erscheint die Karte auf der öffentlichen Partner-Seite -->
|
||||
<div class="adm-pp-preview-card hidden" id="adm-pp-preview-${p.user_id}" style="margin:0 0 var(--space-3)">
|
||||
<div class="text-xs-muted" style="margin-bottom:var(--space-2)">
|
||||
${UI.icon('eye')} So erscheint die Karte auf der Partner-Seite:
|
||||
</div>
|
||||
<div class="by-card" style="padding:var(--space-4);position:relative;overflow:hidden;max-width:340px">
|
||||
<div style="position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(135deg,#7c3aed,#a855f7)"></div>
|
||||
<div style="display:flex;align-items:center;gap:var(--space-3)">
|
||||
${p.logo_url
|
||||
? `<img src="${UI.escape(p.logo_url)}" alt="" style="width:56px;height:56px;border-radius:var(--radius-md);object-fit:contain;flex-shrink:0;background:var(--c-surface-2);padding:4px">`
|
||||
: `<div style="width:56px;height:56px;border-radius:50%;flex-shrink:0;background:linear-gradient(135deg,#7c3aed,#a855f7);display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:800;color:#fff">${UI.escape((p.display_name || p.name || '?')[0].toUpperCase())}</div>`}
|
||||
<div class="flex-1-min">
|
||||
<div style="font-weight:700;font-size:var(--text-base)">${UI.escape(p.display_name || p.name)}</div>
|
||||
${p.tagline ? `<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:1px">${UI.escape(p.tagline)}</div>` : ''}
|
||||
<div style="display:flex;flex-wrap:wrap;gap:var(--space-2);margin-top:var(--space-1)">
|
||||
${p.website ? `<a href="${UI.escape(p.website)}" target="_blank" rel="noopener" style="font-size:var(--text-xs);color:var(--c-primary)">🌐 ${UI.escape(p.website.replace(/^https?:\/\//, ''))}</a>` : ''}
|
||||
${p.instagram ? `<span class="text-xs-muted">📸 ${UI.escape(p.instagram)}</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${p.bio ? `<p style="margin:var(--space-3) 0 0;font-size:var(--text-sm);color:var(--c-text-secondary);line-height:1.5">${UI.escape(p.bio)}</p>` : ''}
|
||||
${p.photos?.length ? `
|
||||
<div style="display:grid;grid-template-columns:repeat(${Math.min(p.photos.length, 3)},1fr);gap:var(--space-1);margin-top:var(--space-3);border-radius:var(--radius-md);overflow:hidden">
|
||||
${p.photos.slice(0, 3).map(url => {
|
||||
const isVid = url.endsWith('.mp4') || url.endsWith('.webm');
|
||||
return isVid
|
||||
? `<video src="${UI.escape(url)}" style="width:100%;aspect-ratio:1;object-fit:cover" muted playsinline loop autoplay></video>`
|
||||
: `<img src="${UI.escape(url)}" style="width:100%;aspect-ratio:1;object-fit:cover">`;
|
||||
}).join('')}
|
||||
</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
${p.approved === 1
|
||||
? `<span class="badge" style="background:#dcfce7;color:#16a34a">✓ Frei</span>`
|
||||
: p.approved === -1
|
||||
? `<span class="badge" style="background:#fee2e2;color:#dc2626">✗ Abgelehnt</span>`
|
||||
: p.submitted_at
|
||||
? `<span class="badge" style="background:#fef9c3;color:#a16207">⏳ Prüfen</span>`
|
||||
: `<span class="badge">Entwurf</span>`}
|
||||
${p.approved !== 1 ? `<button class="btn btn-sm btn-primary adm-pp-review" data-uid="${p.user_id}" data-val="1">✓</button>` : ''}
|
||||
${p.approved !== -1 ? `<button class="btn btn-sm btn-ghost adm-pp-review text-danger" data-uid="${p.user_id}" data-val="-1">✗</button>` : ''}
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
|
||||
|
|
@ -2446,6 +2483,13 @@ window.Page_admin = (() => {
|
|||
</div>
|
||||
`;
|
||||
|
||||
// Partner-Profil-Vorschau auf-/zuklappen (.hidden hat !important → classList)
|
||||
el.querySelectorAll('.adm-pp-preview').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
el.querySelector(`#adm-pp-preview-${btn.dataset.uid}`)?.classList.toggle('hidden');
|
||||
});
|
||||
});
|
||||
|
||||
// Partner-Profil freigeben / ablehnen
|
||||
el.querySelectorAll('.adm-pp-review').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<script src="/js/landing-init.js?v=1255"></script>
|
||||
<script src="/js/landing-init.js?v=1256"></script>
|
||||
<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, direkt im Browser.">
|
||||
<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
|
||||
const VER = '1255';
|
||||
const VER = '1256';
|
||||
const CACHE_VERSION = `by-v${VER}`;
|
||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue