Züchter-Bereich (Hub) + Settings-Partner-Karte raus + Admin: alle Code-Einlösungen mit Kanal
- Neue Seite #breeder-dashboard (Welten-Chip 'Züchter' role:breeder in HUND, ersetzt die Einzel-Chips Zuchtkartei + Wurfverw.; beide FABs wandern an den neuen Chip; Läufigkeit bleibt eigener Chip in HUND, Rene-Vorgabe): Zwinger-Karte (Name, verifiziert-Badge, Profil-Editor), Wurfverwaltung mit Wurf-Anzahl, Zuchtkartei mit Hunde-Anzahl. Einzelseiten bleiben erreichbar. - Settings: Partner-Karte entfernt — der 🤝-Welten-Chip ist der Einstieg. - Admin 'Aktive Codes': 👥 zeigt jetzt ALLE Einlösungen eines Codes mit Kanal-Badge (QR #seq aus Kontingent vs. Link/manuell), Datum und Bestätigt-Status — Endpoint /admin/partner/codes/{id}/registrations. Suite: 55 passed.
This commit is contained in:
parent
6a6a09d879
commit
ed7c469c6a
11 changed files with 241 additions and 44 deletions
|
|
@ -2381,6 +2381,10 @@ window.Page_admin = (() => {
|
|||
${c.grants_founder ? '✓' : '—'}
|
||||
</td>
|
||||
<td style="padding:var(--space-2) var(--space-3);white-space:nowrap;text-align:right">
|
||||
${c.uses > 0 ? `
|
||||
<button class="btn btn-ghost btn-sm adm-code-regs" data-id="${c.id}" title="Alle Einlösungen anzeigen (inkl. Kanal)">
|
||||
${UI.icon('users')}
|
||||
</button>` : ''}
|
||||
<button class="btn btn-ghost btn-sm adm-toggle-code" data-id="${c.id}"
|
||||
title="${c.active ? 'Pausieren — Notbremse wenn der Code im Netz kursiert (Einlösung gesperrt, Historie bleibt)' : 'Wieder aktivieren'}"
|
||||
style="font-size:var(--text-xs)">
|
||||
|
|
@ -2392,6 +2396,11 @@ window.Page_admin = (() => {
|
|||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="hidden" id="adm-code-regs-${c.id}">
|
||||
<td colspan="5" style="padding:0 var(--space-3) var(--space-3);background:var(--c-surface-2)">
|
||||
<div class="text-sm-muted" style="padding:var(--space-3) 0">Lädt…</div>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>`
|
||||
|
|
@ -2571,6 +2580,38 @@ window.Page_admin = (() => {
|
|||
</div>
|
||||
`;
|
||||
|
||||
// Alle Einlösungen eines Codes (lazy, .hidden via classList) — mit Kanal-Spalte
|
||||
el.querySelectorAll('.adm-code-regs').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const row = el.querySelector(`#adm-code-regs-${btn.dataset.id}`);
|
||||
if (!row) return;
|
||||
row.classList.toggle('hidden');
|
||||
if (row.classList.contains('hidden') || row.dataset.loaded === '1') return;
|
||||
try {
|
||||
const regs = await API.get(`/admin/partner/codes/${btn.dataset.id}/registrations`);
|
||||
row.dataset.loaded = '1';
|
||||
const cell = row.querySelector('td');
|
||||
cell.innerHTML = !regs.length
|
||||
? `<div class="text-sm-muted" style="padding:var(--space-3) 0">Keine Accounts.</div>`
|
||||
: regs.map(u => `
|
||||
<div style="display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) 0;border-bottom:1px solid var(--c-border);font-size:var(--text-sm)">
|
||||
<div class="flex-1-min">
|
||||
<span style="font-weight:600">${UI.escape(u.name)}</span>
|
||||
<span class="text-xs-muted">· ${UI.escape(u.email)}</span>
|
||||
</div>
|
||||
<span class="badge" style="background:${u.qr_seq ? 'rgba(139,92,246,.12)' : 'var(--c-surface)'};color:${u.qr_seq ? '#8B5CF6' : 'var(--c-text-muted)'}"
|
||||
title="${u.qr_seq ? `Gedruckter QR-Code aus Kontingent „${UI.escape(u.qr_batch_label || '')}"` : 'Code eingetippt oder ?ref-Link'}">
|
||||
${u.qr_seq ? `QR #${u.qr_seq}` : 'Link/manuell'}
|
||||
</span>
|
||||
<span class="text-xs-muted">${(u.created_at || '').slice(0, 16).replace(' ', ' · ')}</span>
|
||||
${u.email_verified
|
||||
? `<span class="badge" style="background:#dcfce7;color:#16a34a">✓ bestätigt</span>`
|
||||
: `<span class="badge" style="background:#fef9c3;color:#a16207">⏳ unbestätigt</span>`}
|
||||
</div>`).join('');
|
||||
} catch (err) { UI.toast.error(err.message); }
|
||||
});
|
||||
});
|
||||
|
||||
// Code pausieren/aktivieren (Notbremse bei geleakten Codes)
|
||||
el.querySelectorAll('.adm-toggle-code').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue