// team.js document.addEventListener('DOMContentLoaded', () => { // ── Tabs ───────────────────────────────────────────────────────────────────── document.querySelectorAll('.crud-tab').forEach(tab => { tab.addEventListener('click', () => { document.querySelectorAll('.crud-tab').forEach(t => t.classList.toggle('crud-tab--active', t === tab) ); document.querySelectorAll('[data-tab-panel]').forEach(panel => { panel.hidden = panel.dataset.tabPanel !== tab.dataset.tab; }); }); }); // ── Einlade-Modal ───────────────────────────────────────────────────────────── const modal = document.getElementById('team-modal'); const errorsBox = document.getElementById('team-modal-errors'); const openModal = () => { modal.hidden = false; }; const closeModal = () => { modal.hidden = true; errorsBox.hidden = true; ['inv-firstName', 'inv-lastName', 'inv-email'].forEach(id => { document.getElementById(id).value = ''; }); const defaultRole = modal.querySelector('input[name="inv-role"][value="member"]'); if (defaultRole) defaultRole.checked = true; }; document.getElementById('team-invite-btn').addEventListener('click', openModal); document.getElementById('team-modal-close').addEventListener('click', closeModal); document.getElementById('team-modal-cancel').addEventListener('click', closeModal); modal.addEventListener('click', e => { if (e.target === modal) closeModal(); }); document.getElementById('team-modal-submit').addEventListener('click', async () => { const payload = { firstName: document.getElementById('inv-firstName').value.trim(), lastName: document.getElementById('inv-lastName').value.trim(), email: document.getElementById('inv-email').value.trim(), role: modal.querySelector('input[name="inv-role"]:checked')?.value ?? 'member', }; const res = await fetch('/api/team/invite', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const data = await res.json(); if (!res.ok) { errorsBox.hidden = false; errorsBox.innerHTML = ''; return; } closeModal(); window.location.reload(); }); // ── Listen-Delegation: aktive User + Einladungen ─────────────────────────── const list = document.getElementById('team-list'); if (list) { list.addEventListener('click', e => { const actionEl = e.target.closest('[data-action]'); if (!actionEl) return; const action = actionEl.dataset.action; const row = e.target.closest('.crud-row'); if (!row) return; switch (action) { case 'edit': openEdit(row); break; case 'save': saveEdit(row); break; case 'cancel': closeEdit(row); break; case 'delete': deleteMember(row); break; case 'delete-invite': deleteInvite(actionEl.dataset.id, row); break; } }); } // ── Listen-Delegation: archivierte User ─────────────────────────────────── const archivedList = document.getElementById('team-list-archived'); if (archivedList) { archivedList.addEventListener('click', e => { const actionEl = e.target.closest('[data-action]'); if (!actionEl) return; const row = e.target.closest('.crud-row'); if (!row) return; if (actionEl.dataset.action === 'unarchive') { unarchiveMember(row); } }); } // ── Inline Edit ─────────────────────────────────────────────────────────── function openEdit(row) { row.querySelector('.crud-row__display').hidden = true; row.querySelector('.crud-row__edit').hidden = false; row.querySelector('.edit-first-name')?.focus(); } function closeEdit(row) { row.querySelector('.crud-row__display').hidden = false; row.querySelector('.crud-row__edit').hidden = true; // Felder auf ursprüngliche Werte zurücksetzen row.querySelector('.edit-first-name').value = row.dataset.firstName ?? ''; row.querySelector('.edit-last-name').value = row.dataset.lastName ?? ''; row.querySelector('.edit-email').value = row.dataset.email ?? ''; row.querySelector('.edit-note').value = row.dataset.note ?? ''; const currentRole = row.dataset.role; row.querySelectorAll('.edit-role').forEach(radio => { radio.checked = radio.value === currentRole; }); } async function saveEdit(row) { const id = row.dataset.id; const firstName = row.querySelector('.edit-first-name').value.trim(); const lastName = row.querySelector('.edit-last-name').value.trim(); const email = row.querySelector('.edit-email').value.trim(); const note = row.querySelector('.edit-note').value || null; const role = row.querySelector('.edit-role:checked')?.value ?? row.dataset.role; const res = await fetch(`/api/team/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ firstName, lastName, email, note, role }), }); if (!res.ok) { const data = await res.json(); alert((data.errors ?? [data.error]).join('\n')); return; } const data = await res.json(); updateDisplay(row, data); closeEdit(row); } function updateDisplay(row, data) { row.querySelector('.crud-row__name').textContent = data.fullName; row.querySelector('.crud-row__meta').textContent = `(${data.roleLabel})`; row.dataset.firstName = data.firstName; row.dataset.lastName = data.lastName; row.dataset.email = data.email; row.dataset.note = data.note ?? ''; row.dataset.role = data.role; // Edit-Felder aktualisieren row.querySelector('.edit-first-name').value = data.firstName; row.querySelector('.edit-last-name').value = data.lastName; row.querySelector('.edit-email').value = data.email; row.querySelector('.edit-note').value = data.note ?? ''; row.querySelectorAll('.edit-role').forEach(radio => { radio.checked = radio.value === data.role; }); } // ── Delete ──────────────────────────────────────────────────────────────── async function deleteMember(row) { if (!confirm('Wirklich entfernen?')) return; const id = row.dataset.id; const res = await fetch(`/api/team/${id}`, { method: 'DELETE' }); if (res.status === 409) { if (confirm('Dieser Benutzer hat Zeiteinträge und kann nicht gelöscht werden.\nStattdessen archivieren?')) { await archiveMember(row); } return; } if (!res.ok) { const data = await res.json(); alert(data.error ?? 'Fehler beim Löschen.'); return; } row.classList.add('crud-row--removing'); setTimeout(() => row.remove(), 280); } async function deleteInvite(id, row) { if (!confirm('Einladung zurückziehen?')) return; const res = await fetch(`/api/team/invite/${id}`, { method: 'DELETE' }); if (!res.ok) { const data = await res.json(); alert(data.error ?? 'Fehler'); return; } row.classList.add('crud-row--removing'); setTimeout(() => row.remove(), 280); } // ── Archive / Unarchive ─────────────────────────────────────────────────── async function archiveMember(row) { const id = row.dataset.id; const res = await fetch(`/api/team/${id}/archive`, { method: 'PATCH' }); if (!res.ok) { alert('Fehler beim Archivieren.'); return; } row.classList.add('crud-row--removing'); setTimeout(() => window.location.reload(), 280); } async function unarchiveMember(row) { const id = row.dataset.id; const res = await fetch(`/api/team/${id}/unarchive`, { method: 'PATCH' }); if (!res.ok) { alert('Fehler beim Wiederherstellen.'); return; } row.classList.add('crud-row--removing'); setTimeout(() => window.location.reload(), 280); } });