Feature: Referral-Rabattstufen — 10→20%, 20→30%, 50→50% lebenslang
- auth.py: _referral_tier() + _referral_next() Tier-Logik, GET /api/auth/referral gibt discount_pct + next_tier zurück - settings.js: Referral-UI komplett neu — Tier-Kacheln, Fortschrittsbalken, Zähler, Rabatt-Hinweis - SW by-v517, APP_VER 494
This commit is contained in:
parent
ab41af470d
commit
e7e4adaa70
4 changed files with 88 additions and 29 deletions
|
|
@ -1100,50 +1100,88 @@ window.Page_settings = (() => {
|
|||
if (!el) return;
|
||||
try {
|
||||
const r = await API.auth.referral();
|
||||
|
||||
const TIERS = [{t:10,d:20},{t:20,d:30},{t:50,d:50}];
|
||||
const currentTier = r.discount_pct;
|
||||
const next = r.next_tier;
|
||||
|
||||
// Fortschrittsbalken-Berechnung
|
||||
let barPct = 0, barLabel = '';
|
||||
if (!next) {
|
||||
barPct = 100;
|
||||
barLabel = 'Maximaler Rabatt erreicht!';
|
||||
} else {
|
||||
const prevT = TIERS.find(t => t.d === currentTier)?.t || 0;
|
||||
barPct = Math.round(((r.count - prevT) / (next.count - prevT)) * 100);
|
||||
barLabel = `Noch ${next.count - r.count} ${next.count - r.count === 1 ? 'Person' : 'Personen'} bis ${next.discount}% Rabatt`;
|
||||
}
|
||||
|
||||
el.innerHTML = `
|
||||
<!-- Tier-Übersicht -->
|
||||
<div style="display:flex;gap:var(--space-2);margin-bottom:var(--space-4)">
|
||||
${TIERS.map(({t, d}) => {
|
||||
const reached = r.count >= t;
|
||||
return `<div style="flex:1;padding:var(--space-2) var(--space-1);border-radius:var(--radius-md);
|
||||
text-align:center;border:2px solid ${reached ? '#7c3aed' : 'var(--c-border)'};
|
||||
background:${reached ? 'rgba(124,58,237,.08)' : 'var(--c-surface-2)'}">
|
||||
<div style="font-size:var(--text-lg);font-weight:800;color:${reached ? '#7c3aed' : 'var(--c-text-muted)'}">
|
||||
${d}%
|
||||
</div>
|
||||
<div style="font-size:10px;color:var(--c-text-muted)">ab ${t} Freunden</div>
|
||||
${reached ? `<div style="font-size:10px;font-weight:700;color:#7c3aed">✓ Erreicht</div>` : ''}
|
||||
</div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
|
||||
<!-- Zähler + Fortschritt -->
|
||||
<div style="margin-bottom:var(--space-4)">
|
||||
<div style="display:flex;justify-content:space-between;align-items:baseline;margin-bottom:var(--space-1)">
|
||||
<span style="font-size:var(--text-sm);font-weight:600">
|
||||
${UI.icon('users')} <strong>${r.count}</strong> ${r.count === 1 ? 'Freund geworben' : 'Freunde geworben'}
|
||||
</span>
|
||||
${currentTier > 0 ? `<span style="font-size:var(--text-xs);font-weight:700;color:#7c3aed">${currentTier}% Rabatt aktiv</span>` : ''}
|
||||
</div>
|
||||
<div style="background:var(--c-surface-2);border-radius:var(--radius-full);height:10px;overflow:hidden">
|
||||
<div style="background:linear-gradient(90deg,#7c3aed,#a855f7);width:${barPct}%;height:100%;
|
||||
border-radius:var(--radius-full);transition:width .5s ease"></div>
|
||||
</div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:var(--space-1)">${barLabel}</div>
|
||||
</div>
|
||||
|
||||
<!-- Link + QR -->
|
||||
<div style="display:flex;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3)">
|
||||
<div style="flex:1;min-width:0;background:var(--c-surface-2);border-radius:var(--radius-md);
|
||||
padding:var(--space-2) var(--space-3);font-family:monospace;font-size:var(--text-sm);
|
||||
overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${r.link}</div>
|
||||
<button class="btn btn-primary btn-sm" id="ref-share-btn">${UI.icon('arrow-square-out')} Teilen</button>
|
||||
</div>
|
||||
|
||||
<!-- QR-Code -->
|
||||
<div style="display:flex;justify-content:center;margin-bottom:var(--space-4)">
|
||||
<div style="position:relative;width:160px;height:160px">
|
||||
<div id="ref-qr" style="width:160px;height:160px;border-radius:var(--radius-md);overflow:hidden"></div>
|
||||
<div style="display:flex;justify-content:center;margin-bottom:var(--space-1)">
|
||||
<div style="position:relative;width:140px;height:140px">
|
||||
<div id="ref-qr" style="width:140px;height:140px;border-radius:var(--radius-md);overflow:hidden"></div>
|
||||
<img src="/icons/icon-180.png" alt=""
|
||||
style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
|
||||
width:36px;height:36px;border-radius:8px;border:2px solid #fff">
|
||||
width:32px;height:32px;border-radius:7px;border:2px solid #fff">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="font-size:var(--text-sm);color:var(--c-text-secondary)">
|
||||
${UI.icon('users')} <strong>${r.count}</strong> ${r.count === 1 ? 'Person' : 'Personen'} über deinen Link registriert
|
||||
</div>
|
||||
${r.count > 0 ? `
|
||||
<div style="margin-top:var(--space-2);display:flex;gap:var(--space-2);flex-wrap:wrap">
|
||||
${r.count >= 1 ? `<span class="badge badge-primary">${UI.icon('star')} Botschafter</span>` : ''}
|
||||
${r.count >= 5 ? `<span class="badge badge-primary">${UI.icon('star')} Super-Botschafter</span>` : ''}
|
||||
${r.count >= 10 ? `<span class="badge badge-primary">${UI.icon('star')} Top-Botschafter</span>` : ''}
|
||||
</div>` : ''}
|
||||
<p style="font-size:var(--text-xs);color:var(--c-text-muted);text-align:center;margin:0">
|
||||
Der Rabatt gilt für dich — sobald Bezahlfunktionen aktiv sind, dauerhaft und automatisch.
|
||||
</p>
|
||||
`;
|
||||
|
||||
document.getElementById('ref-share-btn')?.addEventListener('click', async () => {
|
||||
const msg = `Ich bin bei Ban Yaro — der coolsten Hunde-App! Registrier dich mit meinem Link und wir wachsen zusammen 🐾`;
|
||||
if (navigator.share) {
|
||||
navigator.share({ title: 'Ban Yaro — Die Hunde-App', text: 'Schau dir Ban Yaro an!', url: r.link }).catch(() => {});
|
||||
navigator.share({ title: 'Ban Yaro', text: msg, url: r.link }).catch(() => {});
|
||||
} else {
|
||||
await navigator.clipboard.writeText(r.link);
|
||||
UI.toast.success('Link kopiert!');
|
||||
}
|
||||
});
|
||||
// QR-Code rendern (Bibliothek lazy laden)
|
||||
|
||||
await App.loadScript('/js/qrcode.min.js');
|
||||
new QRCode(document.getElementById('ref-qr'), {
|
||||
text: r.link,
|
||||
width: 160,
|
||||
height: 160,
|
||||
colorDark: '#000000',
|
||||
colorLight: '#ffffff',
|
||||
text: r.link, width: 140, height: 140,
|
||||
colorDark: '#000000', colorLight: '#ffffff',
|
||||
correctLevel: QRCode.CorrectLevel.H,
|
||||
});
|
||||
} catch { el.innerHTML = ''; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue