sdasd
This commit is contained in:
@@ -5,6 +5,9 @@ const state = {
|
||||
rotate: { bridge: false, sender: false, external: false },
|
||||
users: [],
|
||||
userMap: new Map(),
|
||||
senders: [],
|
||||
senderMap: new Map(),
|
||||
bridgeTables: [],
|
||||
currentTab: 'profile',
|
||||
loading: false,
|
||||
};
|
||||
@@ -15,6 +18,10 @@ let passwordForm;
|
||||
let settingsForm;
|
||||
let teamTable;
|
||||
let userForm;
|
||||
let senderTable;
|
||||
let senderForm;
|
||||
let bridgePreview;
|
||||
let validateBridgeBtn;
|
||||
|
||||
export function initUserPanel() {
|
||||
avatarBtn = document.getElementById('btn-user');
|
||||
@@ -27,15 +34,25 @@ export function initAccountPage() {
|
||||
settingsForm = document.getElementById('settingsForm');
|
||||
teamTable = document.getElementById('teamTable');
|
||||
userForm = document.getElementById('userForm');
|
||||
senderTable = document.getElementById('senderTable');
|
||||
senderForm = document.getElementById('senderForm');
|
||||
bridgePreview = document.getElementById('bridgeTablesPreview');
|
||||
validateBridgeBtn = document.getElementById('btn-validate-bridge');
|
||||
|
||||
document.getElementById('btn-user-add')?.addEventListener('click', () => openUserForm());
|
||||
document.getElementById('userFormCancel')?.addEventListener('click', () => closeUserForm());
|
||||
userForm?.addEventListener('submit', submitUserForm);
|
||||
|
||||
document.getElementById('btn-sender-add')?.addEventListener('click', () => openSenderForm());
|
||||
document.getElementById('senderFormCancel')?.addEventListener('click', () => closeSenderForm());
|
||||
senderForm?.addEventListener('submit', submitSenderForm);
|
||||
|
||||
profileForm?.addEventListener('submit', submitProfileForm);
|
||||
passwordForm?.addEventListener('submit', submitPasswordForm);
|
||||
settingsForm?.addEventListener('submit', submitSettingsForm);
|
||||
teamTable?.addEventListener('click', handleTeamTableClick);
|
||||
senderTable?.addEventListener('click', handleSenderTableClick);
|
||||
validateBridgeBtn?.addEventListener('click', validateBridgeSettings);
|
||||
|
||||
document.querySelectorAll('[data-user-tab]').forEach(btn => {
|
||||
btn.addEventListener('click', () => switchTab(btn.getAttribute('data-user-tab')));
|
||||
@@ -60,12 +77,18 @@ export function initAccountPage() {
|
||||
|
||||
switchTab(state.currentTab);
|
||||
loadAccountData();
|
||||
updateRoleVisibility();
|
||||
}
|
||||
|
||||
function isOwner() {
|
||||
return (window.__currentUser?.role || '').toLowerCase() === 'owner';
|
||||
}
|
||||
|
||||
function isAdmin() {
|
||||
const role = (window.__currentUser?.role || '').toLowerCase();
|
||||
return role === 'owner' || role === 'admin';
|
||||
}
|
||||
|
||||
function updateAvatar() {
|
||||
const target = document.getElementById('userAvatar');
|
||||
if (!target) return;
|
||||
@@ -73,7 +96,19 @@ function updateAvatar() {
|
||||
target.textContent = name ? name.trim().charAt(0).toUpperCase() : 'U';
|
||||
}
|
||||
|
||||
function updateOwnerVisibility() {
|
||||
function updateRoleVisibility() {
|
||||
const role = (window.__currentUser?.role || '').toLowerCase();
|
||||
document.querySelectorAll('[data-role]').forEach(el => {
|
||||
const allowed = (el.getAttribute('data-role') || '').split(/[\s,]+/).filter(Boolean).map(r => r.toLowerCase());
|
||||
if (!allowed.length) return;
|
||||
const visible = allowed.some(targetRole => {
|
||||
if (targetRole === 'owner') return role === 'owner';
|
||||
if (targetRole === 'admin') return role === 'owner' || role === 'admin';
|
||||
if (targetRole === 'editor') return role === 'owner' || role === 'admin' || role === 'editor';
|
||||
return true;
|
||||
});
|
||||
el.classList.toggle('hidden', !visible);
|
||||
});
|
||||
document.querySelectorAll('.owner-only').forEach(el => {
|
||||
el.classList.toggle('hidden', !isOwner());
|
||||
});
|
||||
@@ -100,13 +135,20 @@ async function loadAccountData() {
|
||||
if (res.user) {
|
||||
window.__currentUser = res.user;
|
||||
updateAvatar();
|
||||
updateOwnerVisibility();
|
||||
updateRoleVisibility();
|
||||
}
|
||||
fillProfileForm(res.user);
|
||||
fillSettingsForm(res.settings || {});
|
||||
if (isOwner()) {
|
||||
await loadUsers();
|
||||
}
|
||||
if (isAdmin()) {
|
||||
await loadSenders();
|
||||
} else {
|
||||
state.senders = [];
|
||||
state.senderMap = new Map();
|
||||
renderSenderList();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast(err.message || 'Fehler beim Laden', false);
|
||||
@@ -128,6 +170,9 @@ function fillSettingsForm(settings) {
|
||||
settingsForm.bridge_token.value = settings.bridge_token || '';
|
||||
settingsForm.sender_token.value = settings.sender_token || '';
|
||||
settingsForm.external_api_token.value = settings.external_api_token || '';
|
||||
const tables = Array.isArray(settings.bridge_tables) ? settings.bridge_tables : [];
|
||||
settingsForm.bridge_tables ? settingsForm.bridge_tables.value = tables.join(', ') : null;
|
||||
applyBridgePreview(tables);
|
||||
state.rotate = { bridge: false, sender: false, external: false };
|
||||
}
|
||||
|
||||
@@ -174,6 +219,7 @@ async function submitSettingsForm(ev) {
|
||||
rotate_bridge_token: state.rotate.bridge ? 1 : 0,
|
||||
rotate_sender_token: state.rotate.sender ? 1 : 0,
|
||||
rotate_external_token: state.rotate.external ? 1 : 0,
|
||||
bridge_tables: parseBridgeTablesInput(),
|
||||
};
|
||||
try {
|
||||
const res = await apiAction('account.settings.update', { method: 'POST', data });
|
||||
@@ -207,6 +253,50 @@ async function downloadFile(type) {
|
||||
}
|
||||
}
|
||||
|
||||
function parseBridgeTablesInput() {
|
||||
if (!settingsForm) return [];
|
||||
const raw = settingsForm.bridge_tables?.value || '';
|
||||
return raw
|
||||
.split(/[\s,]+/)
|
||||
.map(part => part.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function applyBridgePreview(tables) {
|
||||
state.bridgeTables = Array.isArray(tables) ? tables : [];
|
||||
if (!bridgePreview) return;
|
||||
if (!state.bridgeTables.length) {
|
||||
bridgePreview.innerHTML = '<span class="text-xs text-slate-500">Keine Einschränkung – alle Tabellen erlaubt.</span>';
|
||||
return;
|
||||
}
|
||||
bridgePreview.innerHTML = state.bridgeTables.map(name => `<span class="chip">${escapeHtml(name)}</span>`).join('');
|
||||
}
|
||||
|
||||
async function validateBridgeSettings(ev) {
|
||||
ev?.preventDefault();
|
||||
if (!settingsForm) return;
|
||||
const data = {
|
||||
bridge_url: settingsForm.bridge_url.value.trim(),
|
||||
bridge_token: settingsForm.bridge_token.value.trim(),
|
||||
};
|
||||
if (!data.bridge_url || !data.bridge_token) {
|
||||
toast('Bitte Bridge-URL und Token angeben', false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await apiAction('account.bridge.test', { method: 'POST', data });
|
||||
if (!res?.ok) throw new Error(res?.error || 'Prüfung fehlgeschlagen');
|
||||
const tables = Array.isArray(res.tables) ? res.tables : [];
|
||||
applyBridgePreview(tables);
|
||||
if (settingsForm.bridge_tables) {
|
||||
settingsForm.bridge_tables.value = tables.join(', ');
|
||||
}
|
||||
toast('Bridge erfolgreich geprüft', true);
|
||||
} catch (err) {
|
||||
toast(err.message || 'Prüfung fehlgeschlagen', false);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadUsers() {
|
||||
try {
|
||||
const res = await apiAction('account.users.list', { method: 'GET' });
|
||||
@@ -329,6 +419,110 @@ function copyToClipboard(value) {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSenders() {
|
||||
if (!senderTable) return;
|
||||
try {
|
||||
const res = await apiAction('account.senders.list', { method: 'GET' });
|
||||
if (!res?.ok) throw new Error(res?.error || 'Absender konnten nicht geladen werden');
|
||||
state.senders = res.items || [];
|
||||
state.senderMap = new Map(state.senders.map(item => [item.id, item]));
|
||||
renderSenderList();
|
||||
} catch (err) {
|
||||
toast(err.message || 'Fehler beim Laden der Absender', false);
|
||||
}
|
||||
}
|
||||
|
||||
function renderSenderList() {
|
||||
if (!senderTable) return;
|
||||
const tbody = senderTable.querySelector('tbody');
|
||||
if (!tbody) return;
|
||||
if (!state.senders.length) {
|
||||
tbody.innerHTML = '<tr><td colspan="5" class="text-sm text-slate-500">Keine Absender vorhanden.</td></tr>';
|
||||
return;
|
||||
}
|
||||
tbody.innerHTML = state.senders.map(sender => `
|
||||
<tr>
|
||||
<td>${escapeHtml(sender.label || sender.from_name || sender.from_email)}</td>
|
||||
<td>${escapeHtml(sender.from_name || '—')}</td>
|
||||
<td>${escapeHtml(sender.from_email)}</td>
|
||||
<td>${escapeHtml(sender.reply_to || '')}</td>
|
||||
<td class="text-right flex gap-2 justify-end">
|
||||
<button class="btn" data-sender-action="edit" data-sender-id="${sender.id}">Bearbeiten</button>
|
||||
<button class="btn btn-danger" data-sender-action="delete" data-sender-id="${sender.id}">Löschen</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function handleSenderTableClick(ev) {
|
||||
const btn = ev.target.closest('button[data-sender-action]');
|
||||
if (!btn) return;
|
||||
const id = Number(btn.getAttribute('data-sender-id'));
|
||||
const action = btn.getAttribute('data-sender-action');
|
||||
const sender = state.senderMap.get(id);
|
||||
if (!sender) return;
|
||||
if (action === 'edit') {
|
||||
openSenderForm(sender);
|
||||
} else if (action === 'delete') {
|
||||
if (confirm(`Absender "${sender.label || sender.from_email}" wirklich löschen?`)) {
|
||||
deleteSender(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function openSenderForm(sender = null) {
|
||||
if (!senderForm) return;
|
||||
senderForm.classList.remove('hidden');
|
||||
senderForm.sender_id.value = sender?.id || '';
|
||||
senderForm.label.value = sender?.label || '';
|
||||
senderForm.from_name.value = sender?.from_name || '';
|
||||
senderForm.from_email.value = sender?.from_email || '';
|
||||
senderForm.reply_to.value = sender?.reply_to || '';
|
||||
}
|
||||
|
||||
function closeSenderForm() {
|
||||
if (!senderForm) return;
|
||||
senderForm.classList.add('hidden');
|
||||
senderForm.reset();
|
||||
senderForm.sender_id.value = '';
|
||||
}
|
||||
|
||||
async function submitSenderForm(ev) {
|
||||
ev.preventDefault();
|
||||
if (!senderForm) return;
|
||||
const payload = {
|
||||
sender_id: senderForm.sender_id.value ? Number(senderForm.sender_id.value) : undefined,
|
||||
label: senderForm.label.value.trim(),
|
||||
from_name: senderForm.from_name.value.trim(),
|
||||
from_email: senderForm.from_email.value.trim(),
|
||||
reply_to: senderForm.reply_to.value.trim(),
|
||||
};
|
||||
if (!payload.from_email) {
|
||||
toast('Bitte eine Absenderadresse angeben', false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await apiAction('account.senders.save', { method: 'POST', data: payload });
|
||||
if (!res?.ok) throw new Error(res?.error || 'Speichern fehlgeschlagen');
|
||||
closeSenderForm();
|
||||
await loadSenders();
|
||||
toast('Absender gespeichert', true);
|
||||
} catch (err) {
|
||||
toast(err.message || 'Fehler beim Speichern', false);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteSender(senderId) {
|
||||
try {
|
||||
const res = await apiAction('account.senders.delete', { method: 'POST', data: { sender_id: senderId } });
|
||||
if (!res?.ok) throw new Error(res?.error || 'Löschen fehlgeschlagen');
|
||||
await loadSenders();
|
||||
toast('Absender gelöscht', true);
|
||||
} catch (err) {
|
||||
toast(err.message || 'Fehler beim Löschen', false);
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str || '')
|
||||
.replace(/&/g, '&')
|
||||
|
||||
Reference in New Issue
Block a user