Refactor: 1167 _esc() → UI.escape() in 36 Dateien, SW by-v1113
Bündel 1 aus dem Duplikat-Audit: existierende zentrale Helper nutzen
statt lokale Duplikate.
Pure Migration ohne neuen Code:
- 1167 _esc()-Aufrufe in 36 Page-Modulen migriert auf UI.escape()
- 24 lokale _esc/_escape-Definitionen entfernt
- lost.js hatte _escape() (Variante) — 17 Aufrufe ebenfalls migriert
- jobs.js + breeder.js: tote Alias-Wrapper entfernt
UI.escape() existierte schon — wurde nur überall lokal nochmal
implementiert. Funktional identisch (gleiche 4-replace-chain für
& < > ").
Tests 19/19 grün. Frontend-LOC um ~120 Zeilen reduziert.
Hinweis: _emptyState (7 Stellen) und _icon (8 Stellen) wurden NICHT
migriert — sie haben abweichende Signaturen von UI.emptyState({...})
bzw. UI.icon(name). Eigener Sprint nötig.
This commit is contained in:
parent
e7939ce98e
commit
c517c9281d
42 changed files with 1115 additions and 1341 deletions
|
|
@ -202,7 +202,7 @@ window.Page_admin = (() => {
|
|||
// Manager-Tabelle
|
||||
const managerRows = d.managers.map(m => `
|
||||
<tr>
|
||||
<td style="font-weight:600">${_esc(m.name)}</td>
|
||||
<td style="font-weight:600">${UI.escape(m.name)}</td>
|
||||
<td class="text-right">${m.published}</td>
|
||||
<td class="text-right">${m.with_link}
|
||||
${m.published > 0 ? `<span style="font-size:10px;color:var(--c-text-muted)">
|
||||
|
|
@ -241,13 +241,13 @@ window.Page_admin = (() => {
|
|||
<tr>
|
||||
<td style="color:var(--c-text-muted);white-space:nowrap">${_fmt(p.published_at)}</td>
|
||||
<td style="font-weight:500;max-width:200px;overflow:hidden;text-overflow:ellipsis;
|
||||
white-space:nowrap">${_esc(p.topic)}</td>
|
||||
white-space:nowrap">${UI.escape(p.topic)}</td>
|
||||
<td>${_PL[p.platform]||p.platform||'–'}</td>
|
||||
<td style="font-size:10px;color:var(--c-text-muted)">${_esc(p.category||'–')}</td>
|
||||
<td style="font-size:10px;color:var(--c-text-muted)">${UI.escape(p.category||'–')}</td>
|
||||
<td>${p.ai_score ? '⭐'.repeat(p.ai_score) : '–'}</td>
|
||||
<td style="font-weight:500">${_esc(p.manager||'–')}</td>
|
||||
<td style="font-weight:500">${UI.escape(p.manager||'–')}</td>
|
||||
<td>${p.post_url
|
||||
? `<a href="${_esc(p.post_url)}" target="_blank" rel="noopener"
|
||||
? `<a href="${UI.escape(p.post_url)}" target="_blank" rel="noopener"
|
||||
style="font-size:11px;color:var(--c-primary)">🔗 Link</a>`
|
||||
: `<span style="font-size:11px;color:var(--c-text-muted)">–</span>`}</td>
|
||||
</tr>`).join('');
|
||||
|
|
@ -319,7 +319,7 @@ window.Page_admin = (() => {
|
|||
try { d = await API.get('/admin/analytics'); }
|
||||
catch (err) {
|
||||
el.innerHTML = `<div style="padding:var(--space-4);color:var(--c-danger);font-size:var(--text-sm)">
|
||||
${UI.icon('warning')} Fehler: ${_esc(err.message || String(err))}</div>`;
|
||||
${UI.icon('warning')} Fehler: ${UI.escape(err.message || String(err))}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -396,7 +396,7 @@ window.Page_admin = (() => {
|
|||
const pct = ((p[valKey] ?? 0) / maxV * 100).toFixed(0);
|
||||
return `<div>
|
||||
<div style="display:flex;justify-content:space-between;font-size:var(--text-xs);margin-bottom:3px">
|
||||
<span style="color:var(--c-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:78%">${_esc(p[labelKey] || '—')}</span>
|
||||
<span style="color:var(--c-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:78%">${UI.escape(p[labelKey] || '—')}</span>
|
||||
<span style="color:var(--c-text-secondary);flex-shrink:0;margin-left:var(--space-2)">${fmt(p[valKey] ?? 0)}</span>
|
||||
</div>
|
||||
<div style="height:4px;border-radius:2px;background:var(--c-surface-2)">
|
||||
|
|
@ -549,7 +549,7 @@ window.Page_admin = (() => {
|
|||
box-shadow:0 0 4px ${dot}"></span>
|
||||
<span style="font-size:var(--text-xs);color:var(--c-text-secondary);font-weight:600">${label}</span>
|
||||
<span class="text-xs-muted">·</span>
|
||||
<span style="font-size:var(--text-xs);color:var(--c-text-muted);font-family:monospace">${_esc(model)}</span>
|
||||
<span style="font-size:var(--text-xs);color:var(--c-text-muted);font-family:monospace">${UI.escape(model)}</span>
|
||||
<span style="margin-left:auto;font-size:10px;padding:1px 6px;border-radius:10px;
|
||||
background:var(--c-surface);color:var(--c-text-muted);border:1px solid var(--c-border)">
|
||||
Modus: ${ki.local_reachable ? 'local' : 'cloud'}
|
||||
|
|
@ -641,8 +641,8 @@ window.Page_admin = (() => {
|
|||
</tr></thead>
|
||||
<tbody>
|
||||
${(kiH.top_users).map(u => `<tr>
|
||||
<td style="font-weight:600">${_esc(u.name)}</td>
|
||||
<td class="text-muted">${_esc(u.email.length > 22 ? u.email.split('@')[1] : u.email)}</td>
|
||||
<td style="font-weight:600">${UI.escape(u.name)}</td>
|
||||
<td class="text-muted">${UI.escape(u.email.length > 22 ? u.email.split('@')[1] : u.email)}</td>
|
||||
<td style="color:var(--c-primary);font-weight:600">${u.cloud}</td>
|
||||
<td>${u.total}</td>
|
||||
<td class="text-muted">${u.last_date?.slice(5) || '—'}</td>
|
||||
|
|
@ -837,24 +837,24 @@ window.Page_admin = (() => {
|
|||
background:var(--c-surface-2);
|
||||
display:flex;align-items:center;justify-content:center;
|
||||
font-weight:var(--weight-bold);color:var(--c-text-secondary)">
|
||||
${_esc(u.name[0].toUpperCase())}
|
||||
${UI.escape(u.name[0].toUpperCase())}
|
||||
</div>
|
||||
|
||||
<!-- Info -->
|
||||
<div class="flex-1-min">
|
||||
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--c-text)">
|
||||
${_esc(u.name)}
|
||||
${UI.escape(u.name)}
|
||||
${u.is_banned ? `<span style="font-size:10px;padding:1px 5px;border-radius:3px;
|
||||
background:var(--c-danger);color:#fff;margin-left:4px">
|
||||
GESPERRT</span>` : ''}
|
||||
</div>
|
||||
<div class="text-xs-muted">
|
||||
${_esc(u.email)} ·
|
||||
${UI.escape(u.email)} ·
|
||||
<span style="color:${u.rolle === 'admin' ? 'var(--c-danger)' : u.rolle === 'moderator' ? '#f59e0b' : 'var(--c-text-muted)'}">
|
||||
${_esc(u.rolle)}
|
||||
${UI.escape(u.rolle)}
|
||||
</span>
|
||||
· <span style="color:${u.subscription_tier && u.subscription_tier !== 'standard' ? 'var(--c-primary)' : 'var(--c-text-muted)'}">
|
||||
${_esc(u.subscription_tier || 'standard')}
|
||||
${UI.escape(u.subscription_tier || 'standard')}
|
||||
</span>
|
||||
· ${u.dog_count} Hund${u.dog_count !== 1 ? 'e' : ''}
|
||||
· ${u.thread_count} Threads
|
||||
|
|
@ -875,28 +875,28 @@ window.Page_admin = (() => {
|
|||
<!-- Aktionen -->
|
||||
<div style="display:flex;gap:var(--space-1);flex-shrink:0">
|
||||
${u.is_banned
|
||||
? `<button class="btn btn-sm btn-ghost adm-unban" data-uid="${u.id}" data-name="${_esc(u.name)}"
|
||||
? `<button class="btn btn-sm btn-ghost adm-unban" data-uid="${u.id}" data-name="${UI.escape(u.name)}"
|
||||
title="Sperre aufheben" class="text-success">
|
||||
<svg class="ph-icon"><use href="/icons/phosphor.svg#lock-open"></use></svg>
|
||||
</button>`
|
||||
: `<button class="btn btn-sm btn-ghost adm-ban" data-uid="${u.id}" data-name="${_esc(u.name)}"
|
||||
: `<button class="btn btn-sm btn-ghost adm-ban" data-uid="${u.id}" data-name="${UI.escape(u.name)}"
|
||||
title="Sperren" class="text-danger">
|
||||
<svg class="ph-icon"><use href="/icons/phosphor.svg#lock"></use></svg>
|
||||
</button>`
|
||||
}
|
||||
${isAdmin ? `
|
||||
<button class="btn btn-sm btn-ghost adm-rolle" data-uid="${u.id}"
|
||||
data-name="${_esc(u.name)}" data-rolle="${_esc(u.rolle)}"
|
||||
data-name="${UI.escape(u.name)}" data-rolle="${UI.escape(u.rolle)}"
|
||||
title="Rolle ändern">
|
||||
<svg class="ph-icon"><use href="/icons/phosphor.svg#shield"></use></svg>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-ghost adm-tier" data-uid="${u.id}"
|
||||
data-name="${_esc(u.name)}" data-tier="${_esc(u.subscription_tier || 'standard')}"
|
||||
data-name="${UI.escape(u.name)}" data-tier="${UI.escape(u.subscription_tier || 'standard')}"
|
||||
title="Abo-Stufe ändern">
|
||||
<svg class="ph-icon"><use href="/icons/phosphor.svg#star"></use></svg>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-ghost adm-delete" data-uid="${u.id}"
|
||||
data-name="${_esc(u.name)}" title="Löschen"
|
||||
data-name="${UI.escape(u.name)}" title="Löschen"
|
||||
class="text-danger">
|
||||
<svg class="ph-icon"><use href="/icons/phosphor.svg#trash"></use></svg>
|
||||
</button>
|
||||
|
|
@ -1087,18 +1087,18 @@ window.Page_admin = (() => {
|
|||
<div class="flex-1-min">
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:var(--space-1)">
|
||||
${r.resolved ? '✓ Erledigt · ' : ''}
|
||||
${_esc(r.target_type)} #${r.target_id} ·
|
||||
Gemeldet von <strong>${_esc(r.melder_name)}</strong>
|
||||
${UI.escape(r.target_type)} #${r.target_id} ·
|
||||
Gemeldet von <strong>${UI.escape(r.melder_name)}</strong>
|
||||
</div>
|
||||
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||
color:var(--c-text);margin-bottom:var(--space-1)">
|
||||
Grund: ${_esc(r.grund)}
|
||||
Grund: ${UI.escape(r.grund)}
|
||||
</div>
|
||||
${r.content_preview ? `
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);
|
||||
padding:var(--space-2) var(--space-3);background:var(--c-surface-2);
|
||||
border-radius:var(--radius-sm)">
|
||||
${_esc(r.content_preview)}
|
||||
${UI.escape(r.content_preview)}
|
||||
</div>` : ''}
|
||||
</div>
|
||||
<div style="display:flex;flex-direction:column;gap:var(--space-2);flex-shrink:0">
|
||||
|
|
@ -1175,10 +1175,10 @@ window.Page_admin = (() => {
|
|||
<div class="flex-1-min">
|
||||
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||
color:var(--c-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
|
||||
${t.is_deleted ? '<s>' : ''}${_esc(t.titel)}${t.is_deleted ? '</s>' : ''}
|
||||
${t.is_deleted ? '<s>' : ''}${UI.escape(t.titel)}${t.is_deleted ? '</s>' : ''}
|
||||
</div>
|
||||
<div class="text-xs-muted">
|
||||
von ${_esc(t.autor_name)} ·
|
||||
von ${UI.escape(t.autor_name)} ·
|
||||
${t.antworten} Antworten ·
|
||||
${t.is_pinned ? '📌 ' : ''}${t.is_locked ? '🔒 ' : ''}${t.is_deleted ? '🗑 gelöscht' : ''}
|
||||
</div>
|
||||
|
|
@ -1313,8 +1313,8 @@ window.Page_admin = (() => {
|
|||
return `<div style="border-bottom:1px solid var(--c-border);padding:2px 0">` +
|
||||
`<span class="text-muted">${r.t}</span> ` +
|
||||
`<span style="color:${color};font-weight:600">${r.l}</span> ` +
|
||||
`<span class="text-secondary">${_esc(r.n)}</span> ` +
|
||||
`<span>${_esc(r.m)}</span></div>`;
|
||||
`<span class="text-secondary">${UI.escape(r.n)}</span> ` +
|
||||
`<span>${UI.escape(r.m)}</span></div>`;
|
||||
}).join('') || '<span class="text-muted">Keine Einträge</span>';
|
||||
};
|
||||
el.querySelector('#adm-sys-refresh').addEventListener('click', () => {
|
||||
|
|
@ -1385,13 +1385,13 @@ window.Page_admin = (() => {
|
|||
const scoreBar = v => `<span style="color:${scoreColor(v)};font-weight:600">${v.toFixed(1)}</span>`;
|
||||
const rows = d.results.filter(r => !r.error).map(r =>
|
||||
`<tr>
|
||||
<td style="padding:2px 6px">${_esc(r.name)}</td>
|
||||
<td style="padding:2px 6px">${UI.escape(r.name)}</td>
|
||||
<td style="text-align:center;padding:2px 6px">${scoreBar(r.vollstaendigkeit)}</td>
|
||||
<td style="text-align:center;padding:2px 6px">${scoreBar(r.korrektheit)}</td>
|
||||
<td style="text-align:center;padding:2px 6px">${scoreBar(r.sprachqualitaet)}</td>
|
||||
<td style="text-align:center;padding:2px 6px">${scoreBar(r.konsistenz)}</td>
|
||||
<td style="text-align:center;padding:2px 6px;font-weight:700">${scoreBar(r.gesamt)}</td>
|
||||
<td style="padding:2px 6px;color:var(--c-text-muted);font-size:0.9em">${_esc(r.hinweis || '')}</td>
|
||||
<td style="padding:2px 6px;color:var(--c-text-muted);font-size:0.9em">${UI.escape(r.hinweis || '')}</td>
|
||||
</tr>`
|
||||
).join('');
|
||||
box.style.display = 'block';
|
||||
|
|
@ -1459,9 +1459,9 @@ window.Page_admin = (() => {
|
|||
</div>
|
||||
</div>
|
||||
<div style="margin-top:var(--space-3);font-size:var(--text-xs);color:var(--c-text-muted);display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:4px">
|
||||
<span>Python ${_esc(s.python_version)}</span>
|
||||
<span>Python ${UI.escape(s.python_version)}</span>
|
||||
<span style="font-family:monospace;font-size:var(--text-xs);background:var(--c-surface-2);padding:2px 8px;border-radius:4px;color:var(--c-text-secondary)">
|
||||
APP v${typeof APP_VER !== 'undefined' ? APP_VER : '—'} · ${_esc(s.sw_version || '?')}
|
||||
APP v${typeof APP_VER !== 'undefined' ? APP_VER : '—'} · ${UI.escape(s.sw_version || '?')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1504,9 +1504,9 @@ window.Page_admin = (() => {
|
|||
const userRows = topUsers.map(u => {
|
||||
const emailDisplay = (u.email || '').length > 20
|
||||
? '@' + (u.email || '').split('@')[1]
|
||||
: _esc(u.email || '');
|
||||
: UI.escape(u.email || '');
|
||||
return `<tr>
|
||||
<td style="padding:5px 8px;font-weight:500">${_esc(u.name || '–')}</td>
|
||||
<td style="padding:5px 8px;font-weight:500">${UI.escape(u.name || '–')}</td>
|
||||
<td style="padding:5px 8px;color:var(--c-text-muted);font-size:var(--text-xs)">${emailDisplay}</td>
|
||||
<td style="padding:5px 8px;text-align:right;font-weight:600">${u.total ?? 0}</td>
|
||||
<td style="padding:5px 8px;text-align:right;color:var(--c-text-muted);font-size:var(--text-xs)">${u.last_week || '–'}</td>
|
||||
|
|
@ -1549,7 +1549,7 @@ window.Page_admin = (() => {
|
|||
<div style="display:flex;justify-content:space-between;font-size:var(--text-xs);
|
||||
color:var(--c-text-muted);margin-bottom:var(--space-4)">
|
||||
<span>30 Tage</span>
|
||||
<span>${_esc(lastDate)}</span>
|
||||
<span>${UI.escape(lastDate)}</span>
|
||||
</div>
|
||||
|
||||
${topUsers.length ? `
|
||||
|
|
@ -1704,12 +1704,12 @@ window.Page_admin = (() => {
|
|||
</tr></thead><tbody>
|
||||
${zuchterPending.map((z, i) => `
|
||||
<tr style="${i%2===1?'background:var(--c-surface-2)':''}">
|
||||
<td class="adm-td" style="font-weight:var(--weight-semibold)">${_esc(z.rasse_slug)}</td>
|
||||
<td class="adm-td">${_esc(z.name)}${z.zwingername ? `<br><span style="color:var(--c-text-muted);font-size:var(--text-xs)">${_esc(z.zwingername)}</span>` : ''}</td>
|
||||
<td class="adm-td">${_esc([z.plz, z.ort, z.bundesland].filter(Boolean).join(' '))}</td>
|
||||
<td class="adm-td" style="font-weight:var(--weight-semibold)">${UI.escape(z.rasse_slug)}</td>
|
||||
<td class="adm-td">${UI.escape(z.name)}${z.zwingername ? `<br><span style="color:var(--c-text-muted);font-size:var(--text-xs)">${UI.escape(z.zwingername)}</span>` : ''}</td>
|
||||
<td class="adm-td">${UI.escape([z.plz, z.ort, z.bundesland].filter(Boolean).join(' '))}</td>
|
||||
<td class="adm-td">${z.vdh_mitglied ? `<span style="color:var(--c-success);display:flex;align-items:center;gap:2px"><svg class="ph-icon" style="width:14px;height:14px" aria-hidden="true"><use href="/icons/phosphor.svg#check"></use></svg> VDH</span>` : '—'}</td>
|
||||
<td class="adm-td">${_ageLabel(z.created_at)}</td>
|
||||
<td class="adm-td">${z.website ? `<a href="${_esc(z.website)}" target="_blank" style="color:var(--c-primary);font-size:var(--text-xs)">Link</a>` : '—'}</td>
|
||||
<td class="adm-td">${z.website ? `<a href="${UI.escape(z.website)}" target="_blank" style="color:var(--c-primary);font-size:var(--text-xs)">Link</a>` : '—'}</td>
|
||||
<td class="adm-td" style="text-align:right;white-space:nowrap">
|
||||
<button class="btn btn-sm btn-primary adm-zuchter-approve" data-id="${z.id}" style="margin-right:4px"><svg class="ph-icon" style="width:14px;height:14px" aria-hidden="true"><use href="/icons/phosphor.svg#check"></use></svg> Freigeben</button>
|
||||
<button class="btn btn-sm btn-ghost adm-zuchter-delete" data-id="${z.id}" class="text-danger"><svg class="ph-icon" style="width:14px;height:14px" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg></button>
|
||||
|
|
@ -1719,8 +1719,8 @@ window.Page_admin = (() => {
|
|||
}
|
||||
// Züchter-History
|
||||
if (zuchterDone.length) html += _historySection('Züchter-Einreichungen', zuchterDone,
|
||||
z => `<span style="font-weight:600">${_esc(z.name)}</span> · ${_esc(z.rasse_slug)} ·
|
||||
${UI.icon('check-circle')} ${_esc(z.verified_by_name||'?')} · ${(z.verified_at||'').slice(0,10)}`);
|
||||
z => `<span style="font-weight:600">${UI.escape(z.name)}</span> · ${UI.escape(z.rasse_slug)} ·
|
||||
${UI.icon('check-circle')} ${UI.escape(z.verified_by_name||'?')} · ${(z.verified_at||'').slice(0,10)}`);
|
||||
|
||||
// --- Wiki-Foto-Einreichungen ---
|
||||
html += `<h3 style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||
|
|
@ -1737,12 +1737,12 @@ window.Page_admin = (() => {
|
|||
html += `<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--space-4);margin-bottom:var(--space-3)">
|
||||
${fotosPending.map(f => `
|
||||
<div class="card p-4">
|
||||
<img src="${_esc(f.foto_url)}" alt=""
|
||||
<img src="${UI.escape(f.foto_url)}" alt=""
|
||||
style="width:100%;height:140px;object-fit:cover;border-radius:var(--radius-md);margin-bottom:var(--space-3)">
|
||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-sm)">${_esc(f.rasse_name)}</div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:var(--space-2)">von ${_esc(f.user_name)}</div>
|
||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-sm)">${UI.escape(f.rasse_name)}</div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:var(--space-2)">von ${UI.escape(f.user_name)}</div>
|
||||
<div class="mb-3">${_ageLabel(f.created_at)}</div>
|
||||
${f.aktuell_foto ? `<img src="${_esc(f.aktuell_foto)}" alt="Aktuell"
|
||||
${f.aktuell_foto ? `<img src="${UI.escape(f.aktuell_foto)}" alt="Aktuell"
|
||||
style="width:100%;height:80px;object-fit:cover;border-radius:var(--radius-sm);
|
||||
opacity:.5;margin-bottom:var(--space-2)">
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:var(--space-3)">↑ aktuelles Foto</div>` : ''}
|
||||
|
|
@ -1756,10 +1756,10 @@ window.Page_admin = (() => {
|
|||
|
||||
// Fotos-History
|
||||
if (fotosDone.length) html += _historySection('Foto-Einreichungen', fotosDone,
|
||||
f => `<img src="${_esc(f.foto_url)}" style="width:32px;height:32px;object-fit:cover;border-radius:4px;vertical-align:middle;margin-right:6px">
|
||||
<span style="font-weight:600">${_esc(f.rasse_name||'?')}</span> · von ${_esc(f.user_name||'?')} ·
|
||||
f => `<img src="${UI.escape(f.foto_url)}" style="width:32px;height:32px;object-fit:cover;border-radius:4px;vertical-align:middle;margin-right:6px">
|
||||
<span style="font-weight:600">${UI.escape(f.rasse_name||'?')}</span> · von ${UI.escape(f.user_name||'?')} ·
|
||||
${f.status==='approved' ? `${UI.icon('check-circle')} genehmigt` : `${UI.icon('x-circle')} abgelehnt`}
|
||||
${f.reviewed_by_name ? ` von ${_esc(f.reviewed_by_name)}` : ''} · ${(f.reviewed_at||'').slice(0,10)}`);
|
||||
${f.reviewed_by_name ? ` von ${UI.escape(f.reviewed_by_name)}` : ''} · ${(f.reviewed_at||'').slice(0,10)}`);
|
||||
|
||||
// --- Forum-Meldungen ---
|
||||
html += `<h3 style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||
|
|
@ -1780,16 +1780,16 @@ window.Page_admin = (() => {
|
|||
<div style="display:flex;align-items:flex-start;gap:var(--space-3)">
|
||||
<div class="flex-1-min">
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:var(--space-1);display:flex;align-items:center;flex-wrap:wrap;gap:4px">
|
||||
${_esc(r.target_type)} #${r.target_id} · Gemeldet von <strong>${_esc(r.melder_name || '?')}</strong>
|
||||
${UI.escape(r.target_type)} #${r.target_id} · Gemeldet von <strong>${UI.escape(r.melder_name || '?')}</strong>
|
||||
${_ageLabel(r.created_at)}
|
||||
</div>
|
||||
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);margin-bottom:var(--space-1)">
|
||||
Grund: ${_esc(r.grund)}
|
||||
Grund: ${UI.escape(r.grund)}
|
||||
</div>
|
||||
${r.content_preview ? `
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);
|
||||
padding:var(--space-2) var(--space-3);background:var(--c-surface-2);
|
||||
border-radius:var(--radius-sm)">${_esc(r.content_preview)}</div>` : ''}
|
||||
border-radius:var(--radius-sm)">${UI.escape(r.content_preview)}</div>` : ''}
|
||||
</div>
|
||||
<button class="btn btn-sm btn-primary adm-mod-resolve" data-rid="${r.id}" title="Als erledigt markieren">
|
||||
${UI.icon('check')}
|
||||
|
|
@ -1801,8 +1801,8 @@ window.Page_admin = (() => {
|
|||
|
||||
// Meldungen-History
|
||||
if (reportsDone.length) html += _historySection('Forum-Meldungen', reportsDone,
|
||||
r => `${_esc(r.target_type)} #${r.target_id} · ${_esc(r.grund)} · Gemeldet von ${_esc(r.melder_name||'?')} ·
|
||||
${UI.icon('check-circle')} ${_esc(r.resolved_by_name||'?')} · ${(r.resolved_at||'').slice(0,10)}`);
|
||||
r => `${UI.escape(r.target_type)} #${r.target_id} · ${UI.escape(r.grund)} · Gemeldet von ${UI.escape(r.melder_name||'?')} ·
|
||||
${UI.icon('check-circle')} ${UI.escape(r.resolved_by_name||'?')} · ${(r.resolved_at||'').slice(0,10)}`);
|
||||
|
||||
// --- POI-Korrekturen ---
|
||||
html += `<h3 style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||
|
|
@ -1831,11 +1831,11 @@ window.Page_admin = (() => {
|
|||
<tbody>
|
||||
${poiPending.map((e, i) => `
|
||||
<tr style="${i%2===1?'background:var(--c-surface-2)':''}">
|
||||
<td class="adm-td" style="font-weight:var(--weight-semibold)">${_esc(e.poi_name || `OSM #${e.osm_id}`)}</td>
|
||||
<td class="adm-td"><code class="text-xs">${_esc(e.field)}</code></td>
|
||||
<td class="adm-td" style="color:var(--c-text-muted);font-size:var(--text-xs)">${_esc(e.old_value || '—')}</td>
|
||||
<td class="adm-td text-xs">${_esc(e.new_value || '—')}</td>
|
||||
<td class="adm-td text-muted">${_esc(e.einreicher_name || '?')}</td>
|
||||
<td class="adm-td" style="font-weight:var(--weight-semibold)">${UI.escape(e.poi_name || `OSM #${e.osm_id}`)}</td>
|
||||
<td class="adm-td"><code class="text-xs">${UI.escape(e.field)}</code></td>
|
||||
<td class="adm-td" style="color:var(--c-text-muted);font-size:var(--text-xs)">${UI.escape(e.old_value || '—')}</td>
|
||||
<td class="adm-td text-xs">${UI.escape(e.new_value || '—')}</td>
|
||||
<td class="adm-td text-muted">${UI.escape(e.einreicher_name || '?')}</td>
|
||||
<td class="adm-td">${_ageLabel(e.created_at)}</td>
|
||||
<td class="adm-td" style="text-align:right;white-space:nowrap">
|
||||
<button class="btn btn-sm btn-primary adm-poi-approve" data-id="${e.id}" style="margin-right:4px">
|
||||
|
|
@ -1852,12 +1852,12 @@ window.Page_admin = (() => {
|
|||
}
|
||||
// POI-History
|
||||
if (poiDone.length) html += _historySection('POI-Korrekturen', poiDone,
|
||||
e => `<span style="font-weight:600">${_esc(e.poi_name||`OSM #${e.osm_id}`)}</span> ·
|
||||
<code class="text-xs">${_esc(e.field)}</code>:
|
||||
<span style="text-decoration:line-through;color:var(--c-text-muted)">${_esc(e.old_value||'—')}</span> →
|
||||
${_esc(e.new_value||'—')} ·
|
||||
e => `<span style="font-weight:600">${UI.escape(e.poi_name||`OSM #${e.osm_id}`)}</span> ·
|
||||
<code class="text-xs">${UI.escape(e.field)}</code>:
|
||||
<span style="text-decoration:line-through;color:var(--c-text-muted)">${UI.escape(e.old_value||'—')}</span> →
|
||||
${UI.escape(e.new_value||'—')} ·
|
||||
${e.status==='approved' ? `${UI.icon('check-circle')} freigegeben` : `${UI.icon('x-circle')} abgelehnt`}
|
||||
${e.mod_name ? ` von ${_esc(e.mod_name)}` : ''} · ${(e.resolved_at||'').slice(0,10)}`);
|
||||
${e.mod_name ? ` von ${UI.escape(e.mod_name)}` : ''} · ${(e.resolved_at||'').slice(0,10)}`);
|
||||
|
||||
el.innerHTML = html;
|
||||
|
||||
|
|
@ -1990,17 +1990,17 @@ window.Page_admin = (() => {
|
|||
<div class="flex-1-min">
|
||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-sm);
|
||||
color:var(--c-text);margin-bottom:var(--space-1)">
|
||||
${_esc(a.name)}
|
||||
${UI.escape(a.name)}
|
||||
<span style="font-size:var(--text-xs);color:var(--c-text-muted);font-weight:400;margin-left:6px">
|
||||
${_esc(a.email)}
|
||||
${UI.escape(a.email)}
|
||||
</span>
|
||||
</div>
|
||||
<div style="display:flex;flex-wrap:wrap;gap:var(--space-3);font-size:var(--text-xs);
|
||||
color:var(--c-text-secondary);margin-bottom:var(--space-2)">
|
||||
<span>${UI.icon('paw-print')} ${_esc(a.rasse_text || '–')}</span>
|
||||
<span>${UI.icon('house-line')} ${_esc(a.zwingername || '–')}</span>
|
||||
<span>${UI.icon('users')} ${_esc(a.verein || '–')}</span>
|
||||
<span>${UI.icon('map-pin')} ${_esc(a.stadt || '–')}</span>
|
||||
<span>${UI.icon('paw-print')} ${UI.escape(a.rasse_text || '–')}</span>
|
||||
<span>${UI.icon('house-line')} ${UI.escape(a.zwingername || '–')}</span>
|
||||
<span>${UI.icon('users')} ${UI.escape(a.verein || '–')}</span>
|
||||
<span>${UI.icon('map-pin')} ${UI.escape(a.stadt || '–')}</span>
|
||||
<span style="color:${a.vdh_mitglied ? 'var(--c-success)' : 'var(--c-text-muted)'}">
|
||||
${UI.icon('certificate')} VDH: ${a.vdh_mitglied ? 'ja' : 'nein'}
|
||||
</span>
|
||||
|
|
@ -2010,7 +2010,7 @@ window.Page_admin = (() => {
|
|||
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);
|
||||
padding:var(--space-2) var(--space-3);background:var(--c-surface-2);
|
||||
border-radius:var(--radius-sm);margin-top:var(--space-1)">
|
||||
${_esc(a.beschreibung)}
|
||||
${UI.escape(a.beschreibung)}
|
||||
</div>` : ''}
|
||||
</div>
|
||||
|
||||
|
|
@ -2021,11 +2021,11 @@ window.Page_admin = (() => {
|
|||
${UI.icon('file-text')} Dokumente
|
||||
</button>
|
||||
<button class="btn btn-sm btn-primary adm-breeder-approve"
|
||||
data-uid="${a.user_id || a.id}" data-name="${_esc(a.name)}">
|
||||
data-uid="${a.user_id || a.id}" data-name="${UI.escape(a.name)}">
|
||||
${UI.icon('check')} Freischalten
|
||||
</button>
|
||||
<button class="btn btn-sm btn-ghost adm-breeder-reject"
|
||||
data-uid="${a.user_id || a.id}" data-name="${_esc(a.name)}"
|
||||
data-uid="${a.user_id || a.id}" data-name="${UI.escape(a.name)}"
|
||||
class="text-danger">
|
||||
${UI.icon('x')} Ablehnen
|
||||
</button>
|
||||
|
|
@ -2053,11 +2053,11 @@ window.Page_admin = (() => {
|
|||
body: docs.length
|
||||
? `<div class="flex-col-gap-3">
|
||||
${docs.map(d => `
|
||||
<a href="${_esc(API.breeder.documentUrl(uid, d.id))}"
|
||||
<a href="${UI.escape(API.breeder.documentUrl(uid, d.id))}"
|
||||
target="_blank" rel="noopener"
|
||||
class="btn btn-secondary"
|
||||
style="text-align:left;word-break:break-all">
|
||||
${UI.icon('file')} ${_esc(d.filename || d.name || 'Dokument ' + d.id)}
|
||||
${UI.icon('file')} ${UI.escape(d.filename || d.name || 'Dokument ' + d.id)}
|
||||
</a>`).join('')}
|
||||
</div>`
|
||||
: `<p class="text-sm-muted">Keine Dokumente hochgeladen.</p>`,
|
||||
|
|
@ -2155,12 +2155,12 @@ window.Page_admin = (() => {
|
|||
const rows = breeders.map(b => `
|
||||
<tr>
|
||||
<td style="padding:var(--space-2) var(--space-3)">
|
||||
<div style="font-weight:600;font-size:var(--text-sm)">${_esc(b.name)}</div>
|
||||
<div class="text-xs-muted">${_esc(b.email)}</div>
|
||||
<div style="font-weight:600;font-size:var(--text-sm)">${UI.escape(b.name)}</div>
|
||||
<div class="text-xs-muted">${UI.escape(b.email)}</div>
|
||||
</td>
|
||||
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-sm)">${_esc(b.zwingername || '—')}</td>
|
||||
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-xs);color:var(--c-text-secondary)">${_esc(b.rasse_text || '—')}</td>
|
||||
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-xs);color:var(--c-text-secondary)">${_esc(b.stadt || '—')}</td>
|
||||
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-sm)">${UI.escape(b.zwingername || '—')}</td>
|
||||
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-xs);color:var(--c-text-secondary)">${UI.escape(b.rasse_text || '—')}</td>
|
||||
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-xs);color:var(--c-text-secondary)">${UI.escape(b.stadt || '—')}</td>
|
||||
<td style="padding:var(--space-2) var(--space-3);text-align:center;font-size:var(--text-xs)">
|
||||
${b.wuerfe_count || 0} Würfe<br>
|
||||
<span class="text-muted">${b.hunde_count || 0} Hunde</span>
|
||||
|
|
@ -2171,7 +2171,7 @@ window.Page_admin = (() => {
|
|||
</td>
|
||||
<td style="padding:var(--space-2) var(--space-3)">
|
||||
<button class="btn btn-sm btn-ghost adm-breeder-tier-btn"
|
||||
data-uid="${b.id}" data-name="${_esc(b.name)}" data-tier="${_esc(b.subscription_tier || 'standard')}"
|
||||
data-uid="${b.id}" data-name="${UI.escape(b.name)}" data-tier="${UI.escape(b.subscription_tier || 'standard')}"
|
||||
class="text-xs">
|
||||
Abo
|
||||
</button>
|
||||
|
|
@ -2241,17 +2241,17 @@ window.Page_admin = (() => {
|
|||
${jobs.map((j, i) => `
|
||||
<tr style="${i % 2 === 1 ? 'background:var(--c-surface-2)' : ''}">
|
||||
<td class="adm-td" style="font-weight:var(--weight-semibold);color:var(--c-text)">
|
||||
${_esc(j.name)}
|
||||
<div class="adm-job-id">${_esc(j.id)}</div>
|
||||
${UI.escape(j.name)}
|
||||
<div class="adm-job-id">${UI.escape(j.id)}</div>
|
||||
</td>
|
||||
<td class="adm-td" style="color:var(--c-text-secondary);white-space:nowrap">
|
||||
${j.next_run_time ? _formatDateTime(j.next_run_time) : '<span class="text-muted">—</span>'}
|
||||
</td>
|
||||
<td class="adm-td adm-td-trigger">
|
||||
${_esc(j.trigger)}
|
||||
${UI.escape(j.trigger)}
|
||||
</td>
|
||||
<td class="adm-td text-right">
|
||||
<button class="btn btn-sm btn-ghost adm-job-trigger adm-icon-btn" data-id="${_esc(j.id)}" data-name="${_esc(j.name)}"
|
||||
<button class="btn btn-sm btn-ghost adm-job-trigger adm-icon-btn" data-id="${UI.escape(j.id)}" data-name="${UI.escape(j.name)}"
|
||||
title="Jetzt ausführen" class="text-primary">
|
||||
${UI.icon('play')}
|
||||
</button>
|
||||
|
|
@ -2542,11 +2542,11 @@ window.Page_admin = (() => {
|
|||
background:var(--c-bg-elevated);border-radius:var(--radius-md);border:1px solid var(--c-border)">
|
||||
<div class="flex-1-min">
|
||||
<div style="display:flex;align-items:center;gap:var(--space-2)">
|
||||
<span style="font-size:var(--text-sm);font-weight:600">${_esc(t.label)}</span>
|
||||
<span style="font-size:var(--text-sm);font-weight:600">${UI.escape(t.label)}</span>
|
||||
${accountBadge(t.from_account)}
|
||||
</div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">
|
||||
${_esc(t.subject)}
|
||||
${UI.escape(t.subject)}
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:var(--space-2);flex-shrink:0">
|
||||
|
|
@ -2632,9 +2632,9 @@ window.Page_admin = (() => {
|
|||
onmouseover="this.style.background='var(--c-surface-2)'"
|
||||
onmouseout="this.style.background=''">
|
||||
<td class="p-2">${accountBadge(l.from_account)}</td>
|
||||
<td class="p-2">${_esc(l.recipient)}</td>
|
||||
<td style="padding:var(--space-2);color:var(--c-text-secondary)">${_esc(l.subject)}</td>
|
||||
<td style="padding:var(--space-2);color:var(--c-text-muted)">${_esc(l.sent_by_name || '')}</td>
|
||||
<td class="p-2">${UI.escape(l.recipient)}</td>
|
||||
<td style="padding:var(--space-2);color:var(--c-text-secondary)">${UI.escape(l.subject)}</td>
|
||||
<td style="padding:var(--space-2);color:var(--c-text-muted)">${UI.escape(l.sent_by_name || '')}</td>
|
||||
<td style="padding:var(--space-2);color:var(--c-text-muted)">${(l.sent_at||'').slice(0,16).replace('T',' ')}</td>
|
||||
</tr>`).join('')}
|
||||
</tbody>
|
||||
|
|
@ -2650,17 +2650,17 @@ window.Page_admin = (() => {
|
|||
const l = log[Number(row.dataset.logIdx)];
|
||||
if (!l) return;
|
||||
UI.modal.open({
|
||||
title: _esc(l.subject),
|
||||
title: UI.escape(l.subject),
|
||||
body: `
|
||||
<div style="margin-bottom:var(--space-3);font-size:var(--text-sm);color:var(--c-text-muted)">
|
||||
<strong>An:</strong> ${_esc(l.recipient)} ·
|
||||
<strong>Von:</strong> ${_esc(l.from_account)}@banyaro.app ·
|
||||
<strong>An:</strong> ${UI.escape(l.recipient)} ·
|
||||
<strong>Von:</strong> ${UI.escape(l.from_account)}@banyaro.app ·
|
||||
${(l.sent_at||'').slice(0,16).replace('T',' ')}
|
||||
</div>
|
||||
<pre style="white-space:pre-wrap;font-family:inherit;font-size:var(--text-sm);
|
||||
background:var(--c-surface-2);border-radius:var(--radius-md);
|
||||
padding:var(--space-3);max-height:60vh;overflow-y:auto;
|
||||
color:var(--c-text)">${_esc(l.body || '(kein Text gespeichert)')}</pre>`,
|
||||
color:var(--c-text)">${UI.escape(l.body || '(kein Text gespeichert)')}</pre>`,
|
||||
footer: `<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>`,
|
||||
});
|
||||
});
|
||||
|
|
@ -2733,7 +2733,7 @@ window.Page_admin = (() => {
|
|||
<div>
|
||||
<label class="form-label text-xs">Name (intern)</label>
|
||||
<input class="form-control" id="${id}-key" type="text" placeholder="z.B. willkommen_neu"
|
||||
value="${_esc(tpl?.key || '')}" ${isNew ? '' : 'readonly'}>
|
||||
value="${UI.escape(tpl?.key || '')}" ${isNew ? '' : 'readonly'}>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label text-xs">Absender</label>
|
||||
|
|
@ -2746,17 +2746,17 @@ window.Page_admin = (() => {
|
|||
<div>
|
||||
<label class="form-label text-xs">Bezeichnung (sichtbar)</label>
|
||||
<input class="form-control" id="${id}-label" type="text" placeholder="z.B. Willkommensnachricht"
|
||||
value="${_esc(tpl?.label || '')}">
|
||||
value="${UI.escape(tpl?.label || '')}">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label text-xs">Betreff</label>
|
||||
<input class="form-control" id="${id}-subject" type="text"
|
||||
value="${_esc(tpl?.subject || '')}">
|
||||
value="${UI.escape(tpl?.subject || '')}">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label text-xs">Text</label>
|
||||
<textarea id="${id}-body" class="form-control" rows="12"
|
||||
style="font-family:monospace;font-size:var(--text-sm);resize:vertical">${_esc(tpl?.body || '')}</textarea>
|
||||
style="font-family:monospace;font-size:var(--text-sm);resize:vertical">${UI.escape(tpl?.body || '')}</textarea>
|
||||
</div>
|
||||
</form>`,
|
||||
footer: `
|
||||
|
|
@ -2826,14 +2826,14 @@ window.Page_admin = (() => {
|
|||
${_formatDateTime(r.created_at)}
|
||||
</td>
|
||||
<td class="adm-td" style="color:var(--c-text);white-space:nowrap">
|
||||
${_esc(r.admin_name || '—')}
|
||||
${UI.escape(r.admin_name || '—')}
|
||||
</td>
|
||||
<td class="adm-td">
|
||||
<span class="adm-badge-mono">${_esc(r.action)}</span>
|
||||
${r.detail ? `<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${_esc(r.detail)}</div>` : ''}
|
||||
<span class="adm-badge-mono">${UI.escape(r.action)}</span>
|
||||
${r.detail ? `<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${UI.escape(r.detail)}</div>` : ''}
|
||||
</td>
|
||||
<td class="adm-td" style="color:var(--c-text-secondary);font-size:var(--text-xs);white-space:nowrap">
|
||||
${_esc(r.target || '—')}
|
||||
${UI.escape(r.target || '—')}
|
||||
</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
|
|
@ -2889,11 +2889,6 @@ window.Page_admin = (() => {
|
|||
`;
|
||||
}
|
||||
|
||||
function _esc(s) {
|
||||
if (!s) return '';
|
||||
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// BEWERBUNGEN — Social-Media-Job
|
||||
// ------------------------------------------------------------------
|
||||
|
|
@ -2932,19 +2927,19 @@ window.Page_admin = (() => {
|
|||
<div class="card" style="margin-bottom:var(--space-3);padding:var(--space-4)" data-id="${r.id}">
|
||||
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:var(--space-3)">
|
||||
<div class="flex-1-min">
|
||||
<div style="font-weight:700;font-size:var(--text-base)">${_esc(r.name)}
|
||||
${r.username ? `<span style="color:var(--c-text-muted);font-weight:400;font-size:var(--text-sm)">(@${_esc(r.username)})</span>` : ''}
|
||||
<div style="font-weight:700;font-size:var(--text-base)">${UI.escape(r.name)}
|
||||
${r.username ? `<span style="color:var(--c-text-muted);font-weight:400;font-size:var(--text-sm)">(@${UI.escape(r.username)})</span>` : ''}
|
||||
</div>
|
||||
<div style="color:var(--c-text-secondary);font-size:var(--text-sm);margin-top:2px">
|
||||
${_esc(r.email)} · @${_esc(r.social_handle||'—')}
|
||||
${r.dog_name ? ` · 🐕 ${_esc(r.dog_name)} (${_esc(r.dog_rasse||'')})` : ''}
|
||||
${UI.escape(r.email)} · @${UI.escape(r.social_handle||'—')}
|
||||
${r.dog_name ? ` · 🐕 ${UI.escape(r.dog_name)} (${UI.escape(r.dog_rasse||'')})` : ''}
|
||||
</div>
|
||||
<div style="color:var(--c-text-muted);font-size:var(--text-xs);margin-top:2px">
|
||||
${r.created_at?.slice(0,16).replace('T',' ')} · ${r.doc_count} Anhang/Anhänge
|
||||
</div>
|
||||
<div style="margin-top:var(--space-2);font-size:var(--text-sm);color:var(--c-text-secondary);
|
||||
background:var(--c-surface-2);border-radius:var(--radius-md);padding:var(--space-2) var(--space-3)">
|
||||
${_esc((r.motivation||'').slice(0,200))}${(r.motivation||'').length>200?'…':''}
|
||||
${UI.escape((r.motivation||'').slice(0,200))}${(r.motivation||'').length>200?'…':''}
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;flex-direction:column;gap:var(--space-2);min-width:120px">
|
||||
|
|
@ -2976,25 +2971,25 @@ window.Page_admin = (() => {
|
|||
const docsHtml = app.docs?.length
|
||||
? app.docs.map(d => `<a href="/api/jobs/admin/applications/${id}/docs/${d.id}"
|
||||
target="_blank" style="display:block;color:var(--c-primary);font-size:var(--text-sm);margin:4px 0">
|
||||
📎 ${_esc(d.filename)}</a>`).join('')
|
||||
📎 ${UI.escape(d.filename)}</a>`).join('')
|
||||
: '<span class="text-sm-muted">Keine Anhänge</span>';
|
||||
|
||||
UI.modal.open({
|
||||
title: `Bewerbung — ${_esc(app.name)}`,
|
||||
title: `Bewerbung — ${UI.escape(app.name)}`,
|
||||
body: `
|
||||
<div style="display:grid;gap:var(--space-3)">
|
||||
<div><b>E-Mail:</b> ${_esc(app.email)}</div>
|
||||
<div><b>Social:</b> @${_esc(app.social_handle||'—')}</div>
|
||||
${app.dog_name ? `<div><b>Hund:</b> ${_esc(app.dog_name)} (${_esc(app.dog_rasse||'')})</div>` : ''}
|
||||
<div><b>E-Mail:</b> ${UI.escape(app.email)}</div>
|
||||
<div><b>Social:</b> @${UI.escape(app.social_handle||'—')}</div>
|
||||
${app.dog_name ? `<div><b>Hund:</b> ${UI.escape(app.dog_name)} (${UI.escape(app.dog_rasse||'')})</div>` : ''}
|
||||
<div><b>Motivation:</b><br>
|
||||
<div style="background:var(--c-surface-2);border-radius:var(--radius-md);padding:var(--space-3);
|
||||
margin-top:var(--space-1);font-size:var(--text-sm);white-space:pre-wrap">${_esc(app.motivation)}</div>
|
||||
margin-top:var(--space-1);font-size:var(--text-sm);white-space:pre-wrap">${UI.escape(app.motivation)}</div>
|
||||
</div>
|
||||
<div><b>Anhänge:</b><br>${docsHtml}</div>
|
||||
<div>
|
||||
<b>Admin-Notiz:</b>
|
||||
<textarea id="adm-bew-note" class="form-control" rows="2" style="margin-top:var(--space-1)"
|
||||
placeholder="Interne Notiz / Nachricht an Bewerber">${_esc(app.admin_note||'')}</textarea>
|
||||
placeholder="Interne Notiz / Nachricht an Bewerber">${UI.escape(app.admin_note||'')}</textarea>
|
||||
</div>
|
||||
</div>`,
|
||||
footer: `
|
||||
|
|
@ -3052,7 +3047,7 @@ window.Page_admin = (() => {
|
|||
border:1.5px solid var(--c-border);border-radius:var(--radius-md);
|
||||
background:var(--c-surface);color:var(--c-text);font-size:var(--text-sm)">
|
||||
${Object.entries(KAT_LABEL).map(([k,v]) =>
|
||||
`<option value="${k}">${_esc(v)}</option>`
|
||||
`<option value="${k}">${UI.escape(v)}</option>`
|
||||
).join('')}
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -3125,7 +3120,7 @@ window.Page_admin = (() => {
|
|||
text-transform:uppercase;letter-spacing:0.05em;
|
||||
padding:var(--space-2) 0;margin-bottom:var(--space-2);
|
||||
border-bottom:1px solid var(--c-border)">
|
||||
${_esc(label)}
|
||||
${UI.escape(label)}
|
||||
</div>
|
||||
`;
|
||||
for (const a of items) {
|
||||
|
|
@ -3138,7 +3133,7 @@ window.Page_admin = (() => {
|
|||
padding:var(--space-3) var(--space-4)">
|
||||
<span style="flex:1;font-size:var(--text-sm);font-weight:500;
|
||||
${a.aktiv ? '' : 'opacity:0.45;text-decoration:line-through'}">
|
||||
${_esc(a.frage)}
|
||||
${UI.escape(a.frage)}
|
||||
</span>
|
||||
<span style="font-size:var(--text-xs);color:var(--c-text-muted);
|
||||
white-space:nowrap">
|
||||
|
|
@ -3159,7 +3154,7 @@ window.Page_admin = (() => {
|
|||
<button class="btn btn-sm adm-hilfe-del-btn"
|
||||
style="padding:2px 8px;font-size:var(--text-xs);
|
||||
background:var(--c-danger-bg,#fee2e2);color:var(--c-danger,#991b1b)"
|
||||
data-id="${a.id}" data-frage="${_esc(a.frage)}">
|
||||
data-id="${a.id}" data-frage="${UI.escape(a.frage)}">
|
||||
${UI.icon('trash')}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -3177,7 +3172,7 @@ window.Page_admin = (() => {
|
|||
border:1.5px solid var(--c-border);border-radius:var(--radius-md);
|
||||
background:var(--c-surface);color:var(--c-text);font-size:var(--text-sm)">
|
||||
${Object.entries(KAT_LABEL).map(([k,v]) =>
|
||||
`<option value="${k}" ${k === a.kategorie ? 'selected' : ''}>${_esc(v)}</option>`
|
||||
`<option value="${k}" ${k === a.kategorie ? 'selected' : ''}>${UI.escape(v)}</option>`
|
||||
).join('')}
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -3185,7 +3180,7 @@ window.Page_admin = (() => {
|
|||
<label style="font-size:var(--text-xs);font-weight:600;display:block;
|
||||
margin-bottom:4px;color:var(--c-text-secondary)">Frage</label>
|
||||
<input type="text" class="adm-hilfe-edit-frage"
|
||||
value="${_esc(a.frage)}"
|
||||
value="${UI.escape(a.frage)}"
|
||||
style="width:100%;padding:var(--space-2) var(--space-3);
|
||||
border:1.5px solid var(--c-border);border-radius:var(--radius-md);
|
||||
background:var(--c-surface);color:var(--c-text);
|
||||
|
|
@ -3199,7 +3194,7 @@ window.Page_admin = (() => {
|
|||
border:1.5px solid var(--c-border);border-radius:var(--radius-md);
|
||||
background:var(--c-surface);color:var(--c-text);
|
||||
font-size:var(--text-sm);box-sizing:border-box;
|
||||
resize:vertical;font-family:inherit">${_esc(a.antwort)}</textarea>
|
||||
resize:vertical;font-family:inherit">${UI.escape(a.antwort)}</textarea>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:var(--space-3)">
|
||||
<label style="font-size:var(--text-xs);font-weight:600;
|
||||
|
|
@ -3494,8 +3489,8 @@ window.Page_admin = (() => {
|
|||
const topRows = d.top_referrers.map((r, i) => `
|
||||
<tr>
|
||||
<td style="padding:8px 10px;color:var(--c-text-muted);font-weight:600">${i + 1}</td>
|
||||
<td style="padding:8px 10px;font-weight:600">${_esc(r.name)}</td>
|
||||
<td style="padding:8px 10px;color:var(--c-text-secondary);font-size:var(--text-xs)">${_esc(r.email)}</td>
|
||||
<td style="padding:8px 10px;font-weight:600">${UI.escape(r.name)}</td>
|
||||
<td style="padding:8px 10px;color:var(--c-text-secondary);font-size:var(--text-xs)">${UI.escape(r.email)}</td>
|
||||
<td style="padding:8px 10px;text-align:right">
|
||||
<span style="font-size:var(--text-lg);font-weight:800;color:var(--c-primary)">${r.invited_count}</span>
|
||||
</td>
|
||||
|
|
@ -3503,8 +3498,8 @@ window.Page_admin = (() => {
|
|||
|
||||
const recentRows = d.recent_invites.slice(0, 50).map(r => `
|
||||
<tr>
|
||||
<td style="padding:6px 10px;font-weight:500">${_esc(r.name)}</td>
|
||||
<td style="padding:6px 10px;color:var(--c-text-secondary);font-size:var(--text-xs)">${_esc(r.referrer_name)}</td>
|
||||
<td style="padding:6px 10px;font-weight:500">${UI.escape(r.name)}</td>
|
||||
<td style="padding:6px 10px;color:var(--c-text-secondary);font-size:var(--text-xs)">${UI.escape(r.referrer_name)}</td>
|
||||
<td style="padding:6px 10px;color:var(--c-text-muted);font-size:var(--text-xs)">${(r.created_at || '').slice(0, 10)}</td>
|
||||
</tr>`).join('');
|
||||
|
||||
|
|
@ -3575,8 +3570,8 @@ window.Page_admin = (() => {
|
|||
<div class="card" style="padding:var(--space-4);margin-bottom:var(--space-3)">
|
||||
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:var(--space-3);flex-wrap:wrap">
|
||||
<div class="flex-1-min">
|
||||
<div style="font-weight:600;font-size:var(--text-sm)">${_esc(r.name)}</div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:var(--space-2)">${_esc(r.email)}</div>
|
||||
<div style="font-weight:600;font-size:var(--text-sm)">${UI.escape(r.name)}</div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:var(--space-2)">${UI.escape(r.email)}</div>
|
||||
<div style="display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap">
|
||||
${tierBadge(r.tier)}
|
||||
${r.discount_pct > 0 ? `<span style="display:inline-block;padding:1px 8px;border-radius:999px;
|
||||
|
|
@ -3590,7 +3585,7 @@ window.Page_admin = (() => {
|
|||
${r.message ? `<div style="margin-top:var(--space-2);font-size:var(--text-xs);color:var(--c-text-secondary);
|
||||
padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);
|
||||
background:var(--c-surface-raised,rgba(0,0,0,.04))">
|
||||
${_esc(r.message)}
|
||||
${UI.escape(r.message)}
|
||||
</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -3598,15 +3593,15 @@ window.Page_admin = (() => {
|
|||
${r.existing_invoice_id ? `
|
||||
<button class="btn adm-invoice-edit-btn"
|
||||
data-invoice-id="${r.existing_invoice_id}"
|
||||
title="Rechnung ${_esc(r.existing_invoice_number)} (${_esc(r.existing_invoice_status)}) bearbeiten"
|
||||
title="Rechnung ${UI.escape(r.existing_invoice_number)} (${UI.escape(r.existing_invoice_status)}) bearbeiten"
|
||||
style="background:#eab308;color:#1a1a1a;border:none;
|
||||
padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);
|
||||
cursor:pointer;font-size:var(--text-sm);font-weight:600">
|
||||
${UI.icon('receipt')} Rechnung bearbeiten
|
||||
</button>` : `
|
||||
<button class="btn adm-invoice-btn"
|
||||
data-name="${_esc(r.name)}" data-email="${_esc(r.email)}"
|
||||
data-tier="${r.tier}" data-address="${_esc(r.billing_address || '')}"
|
||||
data-name="${UI.escape(r.name)}" data-email="${UI.escape(r.email)}"
|
||||
data-tier="${r.tier}" data-address="${UI.escape(r.billing_address || '')}"
|
||||
data-discount="${r.discount_pct || 0}"
|
||||
data-discount-reason="${r.discount_reason || ''}"
|
||||
data-referral-count="${r.referral_count || 0}"
|
||||
|
|
@ -3615,7 +3610,7 @@ window.Page_admin = (() => {
|
|||
cursor:pointer;font-size:var(--text-sm);font-weight:600">
|
||||
${UI.icon('receipt')} Rechnung erstellen
|
||||
</button>`}
|
||||
<button class="btn adm-fulfill-btn" data-id="${r.id}" data-name="${_esc(r.name)}" data-tier="${r.tier}"
|
||||
<button class="btn adm-fulfill-btn" data-id="${r.id}" data-name="${UI.escape(r.name)}" data-tier="${r.tier}"
|
||||
style="background:#16a34a;color:#fff;border:none;
|
||||
padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);
|
||||
cursor:pointer;font-size:var(--text-sm);font-weight:600">
|
||||
|
|
@ -3627,8 +3622,8 @@ window.Page_admin = (() => {
|
|||
// Erledigte als kompakte Tabellenzeilen
|
||||
const _doneRow = r => `
|
||||
<tr>
|
||||
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-sm)">${_esc(r.name)}<br>
|
||||
<span class="text-xs-muted">${_esc(r.email)}</span></td>
|
||||
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-sm)">${UI.escape(r.name)}<br>
|
||||
<span class="text-xs-muted">${UI.escape(r.email)}</span></td>
|
||||
<td style="padding:var(--space-2) var(--space-3)">${tierBadge(r.tier)}</td>
|
||||
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-xs);color:var(--c-success)">
|
||||
✓ ${r.fulfilled_at?.slice(0,10) || ''}</td>
|
||||
|
|
@ -3841,12 +3836,12 @@ window.Page_admin = (() => {
|
|||
actions.push(`<button class="btn btn-sm btn-ghost adm-inv-edit" data-id="${inv.id}" title="Bearbeiten">
|
||||
${UI.icon('pencil')} Bearbeiten
|
||||
</button>`);
|
||||
actions.push(`<button class="btn btn-sm btn-primary adm-inv-send" data-id="${inv.id}" data-num="${_esc(inv.invoice_number)}" title="Senden">
|
||||
actions.push(`<button class="btn btn-sm btn-primary adm-inv-send" data-id="${inv.id}" data-num="${UI.escape(inv.invoice_number)}" title="Senden">
|
||||
${UI.icon('paper-plane-tilt')} Senden
|
||||
</button>`);
|
||||
}
|
||||
if (inv.status === 'sent') {
|
||||
actions.push(`<button class="btn btn-sm btn-ghost adm-inv-send" data-id="${inv.id}" data-num="${_esc(inv.invoice_number)}" title="Erneut senden"
|
||||
actions.push(`<button class="btn btn-sm btn-ghost adm-inv-send" data-id="${inv.id}" data-num="${UI.escape(inv.invoice_number)}" title="Erneut senden"
|
||||
class="text-muted">
|
||||
${UI.icon('paper-plane-tilt')} Erneut senden
|
||||
</button>`);
|
||||
|
|
@ -3855,7 +3850,7 @@ window.Page_admin = (() => {
|
|||
actions.push(`<button class="btn btn-sm btn-secondary adm-inv-pay" data-id="${inv.id}" data-amount="${inv.amount_gross}" title="Als bezahlt markieren">
|
||||
${UI.icon('check-circle')} Bezahlt
|
||||
</button>`);
|
||||
actions.push(`<button class="btn btn-sm btn-ghost adm-inv-cancel" data-id="${inv.id}" data-num="${_esc(inv.invoice_number)}"
|
||||
actions.push(`<button class="btn btn-sm btn-ghost adm-inv-cancel" data-id="${inv.id}" data-num="${UI.escape(inv.invoice_number)}"
|
||||
class="text-danger" title="Stornieren">
|
||||
${UI.icon('x-circle')} Storno
|
||||
</button>`);
|
||||
|
|
@ -3869,11 +3864,11 @@ window.Page_admin = (() => {
|
|||
return `
|
||||
<tr style="${i % 2 === 1 ? 'background:var(--c-surface-2)' : ''}">
|
||||
<td class="adm-td" style="font-weight:600;font-family:monospace;font-size:var(--text-xs)">
|
||||
${_esc(inv.invoice_number)}
|
||||
${UI.escape(inv.invoice_number)}
|
||||
</td>
|
||||
<td class="adm-td">
|
||||
<div style="font-weight:500">${_esc(inv.recipient_name)}</div>
|
||||
<div class="text-xs-muted">${_esc(inv.recipient_email || '')}</div>
|
||||
<div style="font-weight:500">${UI.escape(inv.recipient_name)}</div>
|
||||
<div class="text-xs-muted">${UI.escape(inv.recipient_email || '')}</div>
|
||||
</td>
|
||||
<td class="adm-td" style="text-align:right;font-weight:700;white-space:nowrap">
|
||||
${_fmtEur(inv.amount_gross)}
|
||||
|
|
@ -4007,12 +4002,12 @@ window.Page_admin = (() => {
|
|||
<div>
|
||||
<label class="form-label text-xs">Empfänger Name *</label>
|
||||
<input class="form-control" name="recipient_name" type="text" required
|
||||
placeholder="Max Muster" value="${_esc(p.recipient_name || '')}">
|
||||
placeholder="Max Muster" value="${UI.escape(p.recipient_name || '')}">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label text-xs">E-Mail</label>
|
||||
<input class="form-control" name="recipient_email" type="email"
|
||||
placeholder="max@example.com" value="${_esc(p.recipient_email || '')}">
|
||||
placeholder="max@example.com" value="${UI.escape(p.recipient_email || '')}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -4024,14 +4019,14 @@ window.Page_admin = (() => {
|
|||
</label>
|
||||
<textarea class="form-control" name="recipient_address" rows="2"
|
||||
placeholder="Musterstr. 1 12345 Berlin"
|
||||
style="resize:vertical;font-family:inherit">${_esc(p.recipient_address || '')}</textarea>
|
||||
style="resize:vertical;font-family:inherit">${UI.escape(p.recipient_address || '')}</textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label text-xs">Leistungszeitraum <span class="text-muted">(optional)</span></label>
|
||||
<input class="form-control" name="service_period" type="text"
|
||||
placeholder="z.B. 15.05.2026 oder einmalige Leistung"
|
||||
value="${_esc(p.service_period || '')}">
|
||||
value="${UI.escape(p.service_period || '')}">
|
||||
</div>
|
||||
|
||||
<!-- Positionen -->
|
||||
|
|
@ -4066,7 +4061,7 @@ window.Page_admin = (() => {
|
|||
<label class="form-label text-xs">Notizen <span class="text-muted">(optional)</span></label>
|
||||
<textarea class="form-control" name="notes" rows="2"
|
||||
style="resize:vertical;font-family:inherit"
|
||||
placeholder="Interne Notiz / Zahlungshinweis">${_esc(p.notes || (!isEdit && !p.recipient_name ? 'Zahlbar innerhalb von 14 Tagen ab Rechnungsdatum.' : ''))}</textarea>
|
||||
placeholder="Interne Notiz / Zahlungshinweis">${UI.escape(p.notes || (!isEdit && !p.recipient_name ? 'Zahlbar innerhalb von 14 Tagen ab Rechnungsdatum.' : ''))}</textarea>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
|
@ -4120,7 +4115,7 @@ window.Page_admin = (() => {
|
|||
itemEl.style.cssText = 'display:grid;grid-template-columns:1fr 60px 100px auto;gap:var(--space-2);align-items:center';
|
||||
itemEl.innerHTML = `
|
||||
<input class="form-control inv-item-desc" type="text" placeholder="Beschreibung *"
|
||||
value="${_esc(desc)}" class="text-sm">
|
||||
value="${UI.escape(desc)}" class="text-sm">
|
||||
<input class="form-control inv-item-qty" type="number" min="1" value="${qty}"
|
||||
style="font-size:var(--text-sm);text-align:right" title="Menge">
|
||||
<input class="form-control inv-item-price" type="number" min="0" step="0.01" value="${price.toFixed(2)}"
|
||||
|
|
@ -4303,7 +4298,7 @@ window.Page_admin = (() => {
|
|||
body: `
|
||||
<form id="${id}" class="flex-col-gap-3">
|
||||
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);margin:0">
|
||||
Rechnung <strong>${_esc(invoiceNum)}</strong> stornieren.
|
||||
Rechnung <strong>${UI.escape(invoiceNum)}</strong> stornieren.
|
||||
</p>
|
||||
<div>
|
||||
<label class="form-label text-xs">Stornierungsgrund *</label>
|
||||
|
|
@ -4360,7 +4355,7 @@ window.Page_admin = (() => {
|
|||
|
||||
const itemsHtml = (inv.items || []).map(item => `
|
||||
<tr>
|
||||
<td style="padding:6px 8px">${_esc(item.description)}</td>
|
||||
<td style="padding:6px 8px">${UI.escape(item.description)}</td>
|
||||
<td style="padding:6px 8px;text-align:right">${item.quantity}</td>
|
||||
<td style="padding:6px 8px;text-align:right">${_fmtEur(item.unit_price)}</td>
|
||||
<td style="padding:6px 8px;text-align:right;font-weight:600">${_fmtEur(item.total)}</td>
|
||||
|
|
@ -4368,15 +4363,15 @@ window.Page_admin = (() => {
|
|||
`).join('');
|
||||
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('receipt')} ${_esc(inv.invoice_number)}`,
|
||||
title: `${UI.icon('receipt')} ${UI.escape(inv.invoice_number)}`,
|
||||
body: `
|
||||
<div style="display:flex;flex-direction:column;gap:var(--space-3);font-size:var(--text-sm)">
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:2px">Empfänger</div>
|
||||
<div style="font-weight:600">${_esc(inv.recipient_name)}</div>
|
||||
${inv.recipient_email ? `<div style="color:var(--c-text-muted);font-size:var(--text-xs)">${_esc(inv.recipient_email)}</div>` : ''}
|
||||
${inv.recipient_address ? `<div style="color:var(--c-text-secondary);font-size:var(--text-xs);white-space:pre-line;margin-top:2px">${_esc(inv.recipient_address)}</div>` : ''}
|
||||
<div style="font-weight:600">${UI.escape(inv.recipient_name)}</div>
|
||||
${inv.recipient_email ? `<div style="color:var(--c-text-muted);font-size:var(--text-xs)">${UI.escape(inv.recipient_email)}</div>` : ''}
|
||||
${inv.recipient_address ? `<div style="color:var(--c-text-secondary);font-size:var(--text-xs);white-space:pre-line;margin-top:2px">${UI.escape(inv.recipient_address)}</div>` : ''}
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:2px">Status</div>
|
||||
|
|
@ -4392,7 +4387,7 @@ window.Page_admin = (() => {
|
|||
${inv.service_period ? `
|
||||
<div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:2px">Leistungszeitraum</div>
|
||||
<div>${_esc(inv.service_period)}</div>
|
||||
<div>${UI.escape(inv.service_period)}</div>
|
||||
</div>` : ''}
|
||||
|
||||
<!-- Positionen -->
|
||||
|
|
@ -4421,7 +4416,7 @@ window.Page_admin = (() => {
|
|||
<div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:2px">Notizen</div>
|
||||
<div style="background:var(--c-surface-2);border-radius:var(--radius-md);padding:var(--space-2) var(--space-3);
|
||||
font-size:var(--text-xs);white-space:pre-wrap">${_esc(inv.notes)}</div>
|
||||
font-size:var(--text-xs);white-space:pre-wrap">${UI.escape(inv.notes)}</div>
|
||||
</div>` : ''}
|
||||
</div>
|
||||
`,
|
||||
|
|
@ -4451,7 +4446,7 @@ window.Page_admin = (() => {
|
|||
|
||||
const monthRows = (cf.monthly || []).map((m, i) => `
|
||||
<tr style="${i % 2 === 1 ? 'background:var(--c-surface-2)' : ''}">
|
||||
<td class="adm-td">${_esc(m.month)}</td>
|
||||
<td class="adm-td">${UI.escape(m.month)}</td>
|
||||
<td class="adm-td text-right">${m.count}</td>
|
||||
<td class="adm-td" style="text-align:right;font-weight:600">${_fmtEur(m.revenue)}</td>
|
||||
</tr>`).join('');
|
||||
|
|
@ -4593,8 +4588,8 @@ window.Page_admin = (() => {
|
|||
? ` <span class="text-xs-muted">(RG: ${_fmtE(inv.amount_gross)})</span>` : '';
|
||||
return `
|
||||
<tr style="${i%2===1?'background:var(--c-surface-2)':''}">
|
||||
<td class="adm-td" style="font-family:monospace;font-size:var(--text-xs);${isStorno?'color:var(--c-danger)':''}">${_esc(inv.invoice_number)}</td>
|
||||
<td class="adm-td">${_esc(inv.recipient_name)}</td>
|
||||
<td class="adm-td" style="font-family:monospace;font-size:var(--text-xs);${isStorno?'color:var(--c-danger)':''}">${UI.escape(inv.invoice_number)}</td>
|
||||
<td class="adm-td">${UI.escape(inv.recipient_name)}</td>
|
||||
<td class="adm-td" style="text-align:right;font-weight:600;${amtColor}">${_fmtE(effectiveAmt)}${amtNote}</td>
|
||||
<td class="adm-td" style="${isStorno?'color:var(--c-danger)':''}">${sL[inv.status]||inv.status}</td>
|
||||
<td class="adm-td text-xs-muted">${_fmtD(inv.created_at)}</td>
|
||||
|
|
@ -4602,7 +4597,7 @@ window.Page_admin = (() => {
|
|||
}).join('');
|
||||
resultEl.innerHTML = `
|
||||
<div style="font-size:var(--text-xs);font-weight:700;color:var(--c-text-secondary);margin-bottom:var(--space-2)">
|
||||
${_esc(data.period || `Q${q} ${year}`)} — ${data.count} Buchung(en) · Summe: ${_fmtE(data.total_gross)}
|
||||
${UI.escape(data.period || `Q${q} ${year}`)} — ${data.count} Buchung(en) · Summe: ${_fmtE(data.total_gross)}
|
||||
</div>
|
||||
<div class="adm-table-scroll">
|
||||
<table class="adm-table">
|
||||
|
|
@ -4622,7 +4617,7 @@ window.Page_admin = (() => {
|
|||
</table>
|
||||
</div>`;
|
||||
} catch (e) {
|
||||
resultEl.innerHTML = `<div style="color:var(--c-danger);font-size:var(--text-xs)">Fehler: ${_esc(e.message)}</div>`;
|
||||
resultEl.innerHTML = `<div style="color:var(--c-danger);font-size:var(--text-xs)">Fehler: ${UI.escape(e.message)}</div>`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue