// 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 = '
' + (data.errors ?? [data.error]).map(e => `- ${e}
`).join('') + '
';
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);
}
});