Sprint 14: Map-Fixes, City-Prewarm, Dog-Animation, Scan-Flash

Karte:
- Frankfurt-Fallback (Zoom 10→14 flyTo) mit _frankfurtTimer-Cancel
  wenn echter Standort eintrifft
- OSM-Tile-Fetch parallelisiert (asyncio.Semaphore(3))
- Bounds-Fix: invalidateSize() + pad(0.15) vor getBounds()
- map-pin-slash Icon für gesperrten Standort
- Scan-Done-Flash: Statusbar-Pill grün bei 100%
- Schnüffelhund: outer div (by-wander X) + inner SVG (by-sniff Y)
  für natürlichere zweiachsige Bewegung

Backend:
- City-Prewarm-Job: ~70 deutsche Großstädte beim Start (+90s) und
  wöchentlich (So 01:00), Fortschritts-Mails alle 5h an ADMIN_EMAIL
- ADMIN_EMAIL Env-Var in .env.example dokumentiert

Bugfixes:
- Profil-Edit: /api/profile → /profile (doppelter Prefix)
- Friends: Mobile-Portrait-Layout (flex-wrap, overflow-x:hidden)
- Trainingspläne: Pills text-wrap (flex + white-space:normal)
This commit is contained in:
rene 2026-04-17 14:06:10 +02:00
parent cd3f118113
commit 6fcf841594
10 changed files with 340 additions and 32 deletions

View file

@ -36,7 +36,7 @@ window.Page_friends = (() => {
const myLink = `${location.origin}/#friends?suche=${encodeURIComponent(myName)}`;
_container.innerHTML = `
<div style="max-width:520px;margin:0 auto;padding:var(--space-4)">
<div style="max-width:520px;margin:0 auto;padding:var(--space-4);overflow-x:hidden">
<!-- Mein Freundes-Link -->
<div class="card" style="margin-bottom:var(--space-5);padding:var(--space-4)">
@ -186,18 +186,20 @@ window.Page_friends = (() => {
${list.map(r => `
<div class="card" style="padding:var(--space-4);margin-bottom:var(--space-3);
border-left:3px solid var(--c-primary)">
<div style="display:flex;align-items:center;gap:var(--space-3)">
<div style="display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap">
${_userAvatar(r.requester_name, r.dogs?.[0], r.avatar_url)}
<div style="flex:1;min-width:0">
<div style="font-weight:var(--weight-semibold);color:var(--c-text)">
<div style="flex:1;min-width:120px">
<div style="font-weight:var(--weight-semibold);color:var(--c-text);
overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
${_esc(r.requester_name)}
</div>
${_dogPills(r.dogs, 2)}
</div>
<div style="display:flex;gap:var(--space-2);flex-shrink:0">
<div style="display:flex;gap:var(--space-2);margin-left:auto">
<button class="btn btn-primary btn-sm"
onclick="Page_friends._accept(${r.id})" title="Annehmen">
<svg class="ph-icon"><use href="/icons/phosphor.svg#check"></use></svg>
Annehmen
</button>
<button class="btn btn-ghost btn-sm"
onclick="Page_friends._decline(${r.id})" title="Ablehnen">
@ -426,7 +428,7 @@ window.Page_friends = (() => {
</div>`);
}
if (profile.social_link) {
parts.push(`<div style="font-size:var(--text-xs)">
parts.push(`<div style="font-size:var(--text-xs);word-break:break-all">
<a href="${_esc(profile.social_link)}" target="_blank" rel="noopener noreferrer"
style="color:var(--c-primary)">${_esc(profile.social_link)}</a>
</div>`);
@ -508,10 +510,9 @@ window.Page_friends = (() => {
</div>`
: ''}
</div>
<button class="btn btn-primary btn-sm fr-add-btn"
<button class="btn btn-primary btn-sm fr-add-btn" title="Anfrage senden"
data-user-id="${u.id}" data-user-name="${_esc(u.name)}">
<svg class="ph-icon"><use href="/icons/phosphor.svg#user-plus"></use></svg>
Anfrage
</button>
</div>
`).join('')}