Upload new version
This commit is contained in:
@@ -25,6 +25,15 @@ let teamTable;
|
||||
let userForm;
|
||||
let senderTable;
|
||||
let senderForm;
|
||||
let sectionsList;
|
||||
let sectionsCreateForm;
|
||||
let sectionNameInput;
|
||||
let sectionsDeleteDialog;
|
||||
let sectionsDeleteForm;
|
||||
let sectionsDeleteTarget;
|
||||
let sectionsDeleteText;
|
||||
let sectionsDeleteCancel;
|
||||
let sectionDragId = null;
|
||||
let menuInitialized = false;
|
||||
let menuOpen = false;
|
||||
let debugButton;
|
||||
@@ -75,6 +84,14 @@ export function initAccountPage() {
|
||||
adminTablesAddBtn = document.getElementById('adminBridgeTablesAdd');
|
||||
adminTablesRemoveBtn = document.getElementById('adminBridgeTablesRemove');
|
||||
adminLoadBridgeBtn = document.getElementById('btn-admin-load-bridge');
|
||||
sectionsList = document.getElementById('sectionsList');
|
||||
sectionsCreateForm = document.getElementById('sectionsCreateForm');
|
||||
sectionNameInput = document.getElementById('sectionNameInput');
|
||||
sectionsDeleteDialog = document.getElementById('sectionsDeleteDialog');
|
||||
sectionsDeleteForm = document.getElementById('sectionsDeleteForm');
|
||||
sectionsDeleteTarget = document.getElementById('sectionsDeleteTarget');
|
||||
sectionsDeleteText = document.getElementById('sectionsDeleteText');
|
||||
sectionsDeleteCancel = document.getElementById('sectionsDeleteCancel');
|
||||
|
||||
document.getElementById('btn-user-add')?.addEventListener('click', () => openUserForm());
|
||||
document.getElementById('userFormCancel')?.addEventListener('click', () => closeUserForm());
|
||||
@@ -122,6 +139,8 @@ export function initAccountPage() {
|
||||
refreshBridgeTablesFromEndpoint();
|
||||
});
|
||||
|
||||
initSectionsManager();
|
||||
|
||||
window.addEventListener('bridge-setup-updated', (ev) => {
|
||||
const setup = ev?.detail || {};
|
||||
refreshAdminTables(setup.tables || [], state.settings.bridge_tables || []);
|
||||
@@ -132,6 +151,151 @@ export function initAccountPage() {
|
||||
updateRoleVisibility();
|
||||
}
|
||||
|
||||
function initSectionsManager() {
|
||||
if (!sectionsList || !sectionsCreateForm || !sectionNameInput) return;
|
||||
|
||||
sectionsCreateForm.addEventListener('submit', async (ev) => {
|
||||
ev.preventDefault();
|
||||
const name = sectionNameInput.value.trim();
|
||||
if (!name) return;
|
||||
try {
|
||||
const res = await apiAction('sections_config.create', { method: 'POST', data: { name } });
|
||||
if (!res?.ok) throw new Error(res?.error || 'Erstellen fehlgeschlagen');
|
||||
sectionNameInput.value = '';
|
||||
await loadSectionsConfig();
|
||||
toast('Section erstellt', true);
|
||||
} catch (err) {
|
||||
toast(err.message || 'Erstellen fehlgeschlagen', false);
|
||||
}
|
||||
});
|
||||
|
||||
sectionsDeleteCancel && (sectionsDeleteCancel.onclick = () => {
|
||||
sectionsDeleteDialog?.close();
|
||||
});
|
||||
|
||||
sectionsDeleteForm?.addEventListener('submit', async (ev) => {
|
||||
ev.preventDefault();
|
||||
const id = Number(sectionsDeleteForm?.dataset?.sectionId || 0);
|
||||
const target = Number(sectionsDeleteTarget?.value || 0);
|
||||
if (!id || !target) return;
|
||||
try {
|
||||
const res = await apiAction('sections_config.delete', { method: 'POST', data: { id, move_to: target } });
|
||||
if (!res?.ok) throw new Error(res?.error || 'Löschen fehlgeschlagen');
|
||||
sectionsDeleteDialog?.close();
|
||||
await loadSectionsConfig();
|
||||
toast('Section gelöscht', true);
|
||||
} catch (err) {
|
||||
toast(err.message || 'Löschen fehlgeschlagen', false);
|
||||
}
|
||||
});
|
||||
|
||||
loadSectionsConfig();
|
||||
}
|
||||
|
||||
async function loadSectionsConfig() {
|
||||
try {
|
||||
const res = await apiAction('sections_config.list', { method: 'GET' });
|
||||
const items = Array.isArray(res?.items) ? res.items : [];
|
||||
renderSectionsList(items);
|
||||
} catch (err) {
|
||||
toast(err.message || 'Sections konnten nicht geladen werden', false);
|
||||
}
|
||||
}
|
||||
|
||||
function renderSectionsList(items) {
|
||||
if (!sectionsList) return;
|
||||
const rows = items || [];
|
||||
sectionsList.innerHTML = rows.map((item) => {
|
||||
const isTemplate = Number(item.is_template) === 1;
|
||||
const dragAttr = isTemplate ? '' : 'draggable="true"';
|
||||
const badge = isTemplate ? '<span class="text-xs text-sky-700 bg-sky-100 px-2 py-0.5 rounded-full">Fix</span>' : '';
|
||||
const editBtn = isTemplate ? '' : `<button type="button" class="btn text-xs" data-edit="${item.id}">Umbenennen</button>`;
|
||||
const delBtn = isTemplate ? '' : `<button type="button" class="btn btn-danger text-xs" data-del="${item.id}">Löschen</button>`;
|
||||
return `<li class="section-item flex items-center gap-3 border rounded-lg px-3 py-2 bg-white" data-id="${item.id}" ${dragAttr}>
|
||||
<span class="cursor-grab text-slate-400 select-none">☰</span>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">${escapeHtml(item.name || '')}</div>
|
||||
<div class="text-xs text-slate-500">${escapeHtml(item.slug || '')}</div>
|
||||
</div>
|
||||
${badge}
|
||||
<div class="flex gap-2">${editBtn}${delBtn}</div>
|
||||
</li>`;
|
||||
}).join('');
|
||||
|
||||
sectionsList.querySelectorAll('[data-edit]').forEach(btn => btn.addEventListener('click', async () => {
|
||||
const id = Number(btn.dataset.edit || 0);
|
||||
const current = rows.find(r => Number(r.id) === id);
|
||||
if (!current) return;
|
||||
const next = prompt('Neuer Name', current.name || '');
|
||||
if (!next || next.trim() === current.name) return;
|
||||
try {
|
||||
const res = await apiAction('sections_config.update', { method: 'POST', data: { id, name: next.trim() } });
|
||||
if (!res?.ok) throw new Error(res?.error || 'Speichern fehlgeschlagen');
|
||||
await loadSectionsConfig();
|
||||
toast('Section gespeichert', true);
|
||||
} catch (err) {
|
||||
toast(err.message || 'Speichern fehlgeschlagen', false);
|
||||
}
|
||||
}));
|
||||
|
||||
sectionsList.querySelectorAll('[data-del]').forEach(btn => btn.addEventListener('click', () => {
|
||||
const id = Number(btn.dataset.del || 0);
|
||||
const current = rows.find(r => Number(r.id) === id);
|
||||
if (!current) return;
|
||||
const targets = rows.filter(r => Number(r.id) !== id);
|
||||
if (!targets.length) {
|
||||
toast('Keine Ziel-Section verfügbar', false);
|
||||
return;
|
||||
}
|
||||
if (sectionsDeleteText) {
|
||||
sectionsDeleteText.textContent = `Section "${current.name}" löschen?`;
|
||||
}
|
||||
if (sectionsDeleteTarget) {
|
||||
sectionsDeleteTarget.innerHTML = targets
|
||||
.map(r => `<option value="${r.id}">${escapeHtml(r.name || '')}</option>`)
|
||||
.join('');
|
||||
}
|
||||
if (sectionsDeleteForm) {
|
||||
sectionsDeleteForm.dataset.sectionId = String(id);
|
||||
}
|
||||
sectionsDeleteDialog?.showModal?.();
|
||||
}));
|
||||
|
||||
sectionsList.querySelectorAll('[draggable="true"]').forEach(item => {
|
||||
item.addEventListener('dragstart', (ev) => {
|
||||
sectionDragId = item.dataset.id || null;
|
||||
ev.dataTransfer?.setData('text/plain', sectionDragId || '');
|
||||
});
|
||||
item.addEventListener('dragend', () => {
|
||||
sectionDragId = null;
|
||||
});
|
||||
item.addEventListener('dragover', (ev) => {
|
||||
ev.preventDefault();
|
||||
});
|
||||
item.addEventListener('drop', async (ev) => {
|
||||
ev.preventDefault();
|
||||
const targetId = item.dataset.id || null;
|
||||
if (!sectionDragId || !targetId || sectionDragId === targetId) return;
|
||||
const ids = Array.from(sectionsList.querySelectorAll('[data-id]')).map(el => el.getAttribute('data-id'));
|
||||
const fromIndex = ids.indexOf(sectionDragId);
|
||||
const toIndex = ids.indexOf(targetId);
|
||||
if (fromIndex === -1 || toIndex === -1) return;
|
||||
ids.splice(fromIndex, 1);
|
||||
ids.splice(toIndex, 0, sectionDragId);
|
||||
try {
|
||||
const res = await apiAction('sections_config.reorder', { method: 'POST', data: { order: ids } });
|
||||
if (!res?.ok) throw new Error(res?.error || 'Sortierung fehlgeschlagen');
|
||||
await loadSectionsConfig();
|
||||
toast('Sortierung gespeichert', true);
|
||||
} catch (err) {
|
||||
toast(err.message || 'Sortierung fehlgeschlagen', false);
|
||||
} finally {
|
||||
sectionDragId = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function isOwner() {
|
||||
return (window.__currentUser?.role || '').toLowerCase() === 'owner';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user