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