Feature: Schnell-Gassi-Log + Hunde-Visitenkarte mit QR-Code (SW by-v698)
- Worlds-FAB: neuer 'Schnell-Gassi' Button im Gassi-Chip — öffnet schlankes Bottom-Sheet mit Dauer-Toggle (15/30/45/60 min), auto-Wetter aus Cache, postet direkt als Tagebucheintrag typ='gassi' ohne GPS-Tracking - dog-profile.js: 'Visitenkarte teilen' Button öffnet Modal mit gestalteter Karte (Hundefoto, Name, Rasse/Alter, Wohnort) + QR-Code via qrserver.com, Link-kopieren und native Web-Share-API
This commit is contained in:
parent
6e4bf25581
commit
a4e97348ed
2 changed files with 356 additions and 62 deletions
|
|
@ -195,9 +195,18 @@ window.Page_dog_profile = (() => {
|
|||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#notebook"></use></svg>
|
||||
Hundepass
|
||||
</button>` : ''}
|
||||
${!dog.is_guest ? `<button class="btn btn-secondary w-full" id="dp-vcard-btn">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#identification-card"></use></svg>
|
||||
Visitenkarte teilen
|
||||
</button>` : ''}
|
||||
${!dog.is_guest ? `<button class="btn btn-secondary w-full" id="dp-add-dog-btn">
|
||||
+ Weiteren Hund anlegen
|
||||
</button>` : ''}
|
||||
${!dog.is_guest ? `<button class="btn btn-secondary w-full" id="dp-wrapped-btn"
|
||||
style="background:linear-gradient(135deg,#1a1a2e,#16213e);color:#e8c96e;
|
||||
border-color:transparent;font-weight:700">
|
||||
✨ Jahresrückblick ${new Date().getFullYear()}
|
||||
</button>` : ''}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -264,6 +273,14 @@ window.Page_dog_profile = (() => {
|
|||
_showPassportModal(dog);
|
||||
});
|
||||
|
||||
document.getElementById('dp-vcard-btn')?.addEventListener('click', () => {
|
||||
_showVcardModal(dog);
|
||||
});
|
||||
|
||||
document.getElementById('dp-wrapped-btn')?.addEventListener('click', () => {
|
||||
_showWrappedModal(dog);
|
||||
});
|
||||
|
||||
// Edit- und Add-Klicks laufen über Event-Delegation in init() — keine direkten Listener nötig.
|
||||
}
|
||||
|
||||
|
|
@ -750,6 +767,138 @@ window.Page_dog_profile = (() => {
|
|||
// ----------------------------------------------------------
|
||||
// TEILEN
|
||||
// ----------------------------------------------------------
|
||||
// ----------------------------------------------------------
|
||||
// HUNDE-VISITENKARTE MIT QR-CODE
|
||||
// ----------------------------------------------------------
|
||||
function _showVcardModal(dog) {
|
||||
const passportUrl = `https://banyaro.app/hund/${dog.id}`;
|
||||
const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=140x140&color=ffffff&bgcolor=1a2035&data=${encodeURIComponent(passportUrl)}`;
|
||||
|
||||
const user = _appState?.user;
|
||||
const ownerName = user?.name || '';
|
||||
const wohnort = user?.wohnort || '';
|
||||
|
||||
// Alter errechnen
|
||||
let alterStr = '';
|
||||
if (dog.geburtstag) {
|
||||
const birth = new Date(dog.geburtstag + 'T00:00:00');
|
||||
const now = new Date();
|
||||
const years = now.getFullYear() - birth.getFullYear()
|
||||
- (now < new Date(now.getFullYear(), birth.getMonth(), birth.getDate()) ? 1 : 0);
|
||||
alterStr = years < 1
|
||||
? `${Math.max(1, Math.round((now - birth) / (30.5 * 86400000)))} Monate`
|
||||
: years === 1 ? '1 Jahr' : `${years} Jahre`;
|
||||
}
|
||||
|
||||
const metaLine = [dog.rasse, alterStr].filter(Boolean).join(' · ');
|
||||
|
||||
const cardHtml = `
|
||||
<div id="dp-vcard-canvas" style="
|
||||
background:linear-gradient(135deg,#0f1a2b 0%,#1a2a45 60%,#0d2137 100%);
|
||||
border-radius:20px;padding:24px 20px;color:white;
|
||||
font-family:system-ui,-apple-system,sans-serif;
|
||||
position:relative;overflow:hidden;max-width:340px;margin:0 auto;
|
||||
box-shadow:0 8px 32px rgba(0,0,0,0.4)">
|
||||
|
||||
<!-- Deko-Kreis -->
|
||||
<div style="position:absolute;top:-30px;right:-30px;width:120px;height:120px;
|
||||
border-radius:50%;background:rgba(196,132,58,0.12);pointer-events:none"></div>
|
||||
<div style="position:absolute;bottom:-40px;left:-20px;width:100px;height:100px;
|
||||
border-radius:50%;background:rgba(196,132,58,0.07);pointer-events:none"></div>
|
||||
|
||||
<!-- Header -->
|
||||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:18px">
|
||||
${dog.foto_url
|
||||
? `<img src="${_esc(dog.foto_url)}" style="width:52px;height:52px;border-radius:50%;object-fit:cover;
|
||||
border:2px solid rgba(196,132,58,0.6);flex-shrink:0">`
|
||||
: `<div style="width:52px;height:52px;border-radius:50%;background:rgba(196,132,58,0.2);
|
||||
display:flex;align-items:center;justify-content:center;font-size:1.6rem;
|
||||
flex-shrink:0;border:2px solid rgba(196,132,58,0.4)">🐾</div>`}
|
||||
<div>
|
||||
<div style="font-size:1.25rem;font-weight:800;color:#fff;line-height:1.2">${_esc(dog.name)}</div>
|
||||
${metaLine ? `<div style="font-size:0.8rem;color:rgba(255,255,255,0.6);margin-top:2px">${_esc(metaLine)}</div>` : ''}
|
||||
${wohnort ? `<div style="font-size:0.75rem;color:rgba(196,132,58,0.9);margin-top:3px">📍 ${_esc(wohnort)}</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Divider -->
|
||||
<div style="height:1px;background:rgba(255,255,255,0.1);margin-bottom:16px"></div>
|
||||
|
||||
<!-- Owner + QR -->
|
||||
<div style="display:flex;align-items:flex-end;justify-content:space-between;gap:12px">
|
||||
<div style="flex:1;min-width:0">
|
||||
${ownerName ? `<div style="font-size:0.7rem;color:rgba(255,255,255,0.4);text-transform:uppercase;letter-spacing:.06em;margin-bottom:4px">Besitzer</div>
|
||||
<div style="font-size:0.9rem;font-weight:600;color:rgba(255,255,255,0.85)">${_esc(ownerName)}</div>` : ''}
|
||||
<div style="font-size:0.65rem;color:rgba(255,255,255,0.35);margin-top:8px">banyaro.app</div>
|
||||
</div>
|
||||
<div style="flex-shrink:0;text-align:center">
|
||||
<img id="dp-vcard-qr" src="${_esc(qrUrl)}"
|
||||
style="width:80px;height:80px;border-radius:10px;display:block"
|
||||
alt="QR-Code">
|
||||
<div style="font-size:0.6rem;color:rgba(255,255,255,0.35);margin-top:4px">Profil öffnen</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
UI.modal.open({
|
||||
title: 'Visitenkarte',
|
||||
body: `
|
||||
<div style="margin-bottom:var(--space-4)">${cardHtml}</div>
|
||||
<p style="font-size:var(--text-xs);color:var(--c-text-secondary);text-align:center;margin-bottom:0">
|
||||
QR-Code auf NFC-Tag oder Anhänger kleben — jeder kann das Profil von ${_esc(dog.name)} sofort öffnen.
|
||||
</p>
|
||||
`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" id="dp-vcard-copy-btn">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#link"></use></svg>
|
||||
Link kopieren
|
||||
</button>
|
||||
<button class="btn btn-primary" id="dp-vcard-share-btn">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#share-network"></use></svg>
|
||||
Teilen
|
||||
</button>
|
||||
`,
|
||||
});
|
||||
|
||||
// Link kopieren
|
||||
document.getElementById('dp-vcard-copy-btn')?.addEventListener('click', async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(passportUrl);
|
||||
UI.toast.success('Link kopiert!');
|
||||
} catch {
|
||||
const inp = document.createElement('input');
|
||||
inp.value = passportUrl;
|
||||
document.body.appendChild(inp);
|
||||
inp.select();
|
||||
document.execCommand('copy');
|
||||
inp.remove();
|
||||
UI.toast.success('Link kopiert!');
|
||||
}
|
||||
});
|
||||
|
||||
// Native Share API
|
||||
document.getElementById('dp-vcard-share-btn')?.addEventListener('click', async () => {
|
||||
if (navigator.share) {
|
||||
try {
|
||||
await navigator.share({
|
||||
title: `${dog.name} auf Ban Yaro`,
|
||||
text: `Schau dir das Profil von ${dog.name} an!`,
|
||||
url: passportUrl,
|
||||
});
|
||||
} catch {}
|
||||
} else {
|
||||
// Fallback: kopieren
|
||||
try {
|
||||
await navigator.clipboard.writeText(passportUrl);
|
||||
UI.toast.success('Link kopiert!');
|
||||
} catch {
|
||||
UI.toast.error('Teilen nicht verfügbar.');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function _showShareModal(dog) {
|
||||
UI.modal.open({
|
||||
title: `${_esc(dog.name)} teilen`,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue