Profilbild: zufälliger DiceBear-Avatar (Big Smile) pro Kunde

Der Server erzeugt das SVG lokal unter /api/avatar.svg (Seed = Kunden-ID),
es geht also keine Anfrage an dicebear.com. Die Initialen bleiben als
Fallback, bis das Bild geladen ist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-29 16:36:49 +00:00
1 parent e4efdac226
commit 8c9d28c30f
5 files changed
+70 -2

No files matched your search

+20 -2
View File
@@ -1920,6 +1920,20 @@ function showView(name, { updateHash = true } = {}) {
document.dispatchEvent(new CustomEvent('kb:view', { detail: { name } }))
}
/* Profilbild (DiceBear Big Smile vom eigenen Server); die Initialen bleiben stehen, bis das Bild geladen ist,
und falls es nicht laedt. Die Kunden-ID im Query sorgt dafuer, dass "Als Kunde ansehen" nicht das gecachte Bild zeigt. */
function setAvatar(el, initials, seed) {
if (!el) return
el.textContent = initials
el.dataset.initials = initials
if (!seed) return
const img = new Image()
img.alt = ''
img.decoding = 'async'
img.onload = () => { el.textContent = ''; el.appendChild(img); el.classList.add('has-img') }
img.src = `/api/avatar.svg?c=${encodeURIComponent(seed)}`
}
function initialsOf(name) {
const words = String(name || '').split(/\s+/).filter((w) => /^[\p{L}\p{N}]/u.test(w))
const letters = words.length > 1 ? words[0][0] + words[1][0] : (words[0] || '').slice(0, 2)
@@ -1985,7 +1999,7 @@ async function initDashboardPage() {
document.getElementById('customer-name').textContent = displayName
document.getElementById('customer-meta').textContent =
[customer.code ? `Kundennr. ${customer.code}` : '', customer.email].filter(Boolean).join(' · ')
document.getElementById('customer-avatar').textContent = initialsOf(displayName)
setAvatar(document.getElementById('customer-avatar'), initialsOf(displayName), customer.id)
api('/api/contact')
.then((c) => {
@@ -2212,7 +2226,11 @@ function initAccountSheet() {
const fill = () => {
document.getElementById('kb-sheet-name').textContent = document.getElementById('customer-name')?.textContent || 'Mein Konto'
document.getElementById('kb-sheet-meta').textContent = document.getElementById('customer-meta')?.textContent || ''
document.getElementById('kb-sheet-avatar').textContent = avatar.textContent
const sheetAvatar = document.getElementById('kb-sheet-avatar')
const img = avatar.querySelector('img')
sheetAvatar.textContent = img ? '' : avatar.textContent
sheetAvatar.classList.toggle('has-img', Boolean(img))
if (img) sheetAvatar.appendChild(img.cloneNode())
renderInstallOffers()
}
if (typeof sheet.showModal !== 'function') {
+2
View File
@@ -54,6 +54,8 @@
flex: none; width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center;
font-size: 13px; font-weight: 700; background: #26124a; border: 1px solid var(--kb-purple-line);
}
.kb-avatar.has-img { overflow: hidden; }
.kb-avatar img { width: 100%; height: 100%; display: block; border-radius: 50%; }
/* ---------------------------------------------------------------- Raster --- */
.kb-shell {