Feature: Foto-Upload für Mitglieder im Admin-Panel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Heubaum
2026-04-19 14:15:41 +02:00
parent 661d9b117e
commit 3f7e24be32

View File

@@ -214,6 +214,29 @@
</div>
</div>
<!-- Member Photo Modal -->
<div class="modal-overlay" id="modal-member-photo">
<div class="modal">
<div class="modal-header">
<div class="modal-title" id="member-photo-modal-title">Foto bearbeiten</div>
<button class="modal-close" onclick="closeAdminModal('modal-member-photo')">✕</button>
</div>
<input type="hidden" id="member-photo-name">
<div style="display:flex;align-items:center;gap:16px;margin:16px 0;">
<img id="member-photo-thumb" class="photo-thumb" style="width:80px;height:80px;display:none;" src="" alt="">
<div id="member-photo-initials" style="width:80px;height:80px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:white;font-size:1.4rem;font-weight:700;flex-shrink:0;"></div>
<div class="photo-actions" style="flex-direction:column;align-items:flex-start;">
<label class="btn-file-label">📁 Foto wählen<input type="file" id="member-photo-file" accept="image/*" style="display:none" onchange="handleMemberPhotoFile(this)"></label>
<button class="btn btn-danger btn-sm" id="member-photo-remove" onclick="removeMemberPhoto()" style="display:none;margin-top:6px;">✕ Foto entfernen</button>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeAdminModal('modal-member-photo')">Abbrechen</button>
<button class="btn btn-primary" onclick="saveMemberPhoto()">Speichern</button>
</div>
</div>
</div>
<!-- Import from Chapter Modal -->
<div class="modal-overlay" id="modal-import-chapter">
<div class="modal">
@@ -569,16 +592,75 @@ function renderMembersList() {
const photoB64 = ex.photoB64 || ex.offlinePhotoB64 || null;
const div = document.createElement('div');
div.className = 'member-item';
div.style.cursor = 'pointer';
div.title = 'Foto bearbeiten';
div.innerHTML = `${avatarHtml(m.name, photoB64, m.photoUrl, 40)}
<div style="flex:1;min-width:0;">
<div style="font-weight:600;font-size:0.88rem;">${escHtml(m.name)}</div>
<div style="font-size:0.75rem;color:var(--muted);">${escHtml(m.company||'')}${m.specialty?' · '+escHtml(m.specialty):''}</div>
</div>
<span style="font-size:0.72rem;color:${photoB64?'var(--success)':'var(--muted)'};">${photoB64?'✓ Foto':'kein Foto'}</span>`;
<span style="font-size:0.72rem;color:${photoB64?'var(--success)':'#d97706'};">${photoB64?'✓ Foto':'+ Foto'}</span>`;
div.onclick = () => openMemberPhotoModal(m.name, photoB64);
container.appendChild(div);
});
}
let _memberPhotoB64 = null;
function openMemberPhotoModal(name, currentPhotoB64) {
_memberPhotoB64 = currentPhotoB64 || null;
document.getElementById('member-photo-name').value = name;
document.getElementById('member-photo-modal-title').textContent = name;
updateMemberPhotoUI(_memberPhotoB64, name);
openAdminModal('modal-member-photo');
}
function updateMemberPhotoUI(b64, name) {
_memberPhotoB64 = b64;
const thumb = document.getElementById('member-photo-thumb');
const initials = document.getElementById('member-photo-initials');
const removeBtn = document.getElementById('member-photo-remove');
if (b64) {
thumb.src = b64; thumb.style.display = 'block';
initials.style.display = 'none';
removeBtn.style.display = 'inline-flex';
} else {
thumb.src = ''; thumb.style.display = 'none';
initials.style.display = 'flex';
initials.style.background = getColor(name || '');
initials.textContent = getInitials(name || '');
removeBtn.style.display = 'none';
}
}
function handleMemberPhotoFile(input) {
const file = input.files && input.files[0];
if (!file) return;
const name = document.getElementById('member-photo-name').value;
resizePhotoToBase64(file).then(b64 => updateMemberPhotoUI(b64, name)).catch(e => alert(e.message));
input.value = '';
}
function removeMemberPhoto() {
updateMemberPhotoUI(null, document.getElementById('member-photo-name').value);
}
function saveMemberPhoto() {
const name = document.getElementById('member-photo-name').value;
const extras = loadExtras();
const key = extrasKey(name);
if (!extras[key]) extras[key] = {};
if (_memberPhotoB64) {
extras[key].photoB64 = _memberPhotoB64;
} else {
delete extras[key].photoB64;
delete extras[key].offlinePhotoB64;
}
saveExtras(extras);
closeAdminModal('modal-member-photo');
renderMembersList();
}
async function fetchMembersAdmin() {
const statusEl = document.getElementById('members-status');
statusEl.textContent = 'Lade vom Server…'; statusEl.style.color = 'var(--muted)';