fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
632
backend/frontend/static/js/customer.js
Normal file
632
backend/frontend/static/js/customer.js
Normal file
@@ -0,0 +1,632 @@
|
||||
/**
|
||||
* BreakPilot Customer Portal - Slim JavaScript
|
||||
*
|
||||
* Features:
|
||||
* - Login/Register
|
||||
* - My Consents View
|
||||
* - Data Export Request
|
||||
* - Legal Documents
|
||||
* - Theme Toggle
|
||||
*/
|
||||
|
||||
// API Base URLs
|
||||
const CONSENT_SERVICE_URL = 'http://localhost:8081';
|
||||
const BACKEND_URL = '';
|
||||
|
||||
// State
|
||||
let currentUser = null;
|
||||
let authToken = localStorage.getItem('bp_token');
|
||||
|
||||
// Initialize on DOM ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initTheme();
|
||||
initEventListeners();
|
||||
checkAuth();
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// Theme
|
||||
// ============================================================
|
||||
|
||||
function initTheme() {
|
||||
const saved = localStorage.getItem('bp_theme') || 'light';
|
||||
document.body.setAttribute('data-theme', saved);
|
||||
updateThemeIcon(saved);
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
const current = document.body.getAttribute('data-theme');
|
||||
const next = current === 'dark' ? 'light' : 'dark';
|
||||
document.body.setAttribute('data-theme', next);
|
||||
localStorage.setItem('bp_theme', next);
|
||||
updateThemeIcon(next);
|
||||
}
|
||||
|
||||
function updateThemeIcon(theme) {
|
||||
const icon = document.getElementById('theme-icon');
|
||||
if (icon) icon.textContent = theme === 'dark' ? '☀️' : '🌙';
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Event Listeners
|
||||
// ============================================================
|
||||
|
||||
function initEventListeners() {
|
||||
// Theme toggle
|
||||
const themeBtn = document.getElementById('btn-theme');
|
||||
if (themeBtn) themeBtn.addEventListener('click', toggleTheme);
|
||||
|
||||
// Login button
|
||||
const loginBtn = document.getElementById('btn-login');
|
||||
if (loginBtn) loginBtn.addEventListener('click', showLoginModal);
|
||||
|
||||
// Legal button
|
||||
const legalBtn = document.getElementById('btn-legal');
|
||||
if (legalBtn) legalBtn.addEventListener('click', () => showLegalDocument('privacy'));
|
||||
|
||||
// User menu toggle
|
||||
const userMenuBtn = document.getElementById('user-menu-btn');
|
||||
if (userMenuBtn) {
|
||||
userMenuBtn.addEventListener('click', () => {
|
||||
document.getElementById('user-menu').classList.toggle('open');
|
||||
});
|
||||
}
|
||||
|
||||
// Close user menu on outside click
|
||||
document.addEventListener('click', (e) => {
|
||||
const userMenu = document.getElementById('user-menu');
|
||||
if (userMenu && !userMenu.contains(e.target)) {
|
||||
userMenu.classList.remove('open');
|
||||
}
|
||||
});
|
||||
|
||||
// Login form
|
||||
const loginForm = document.getElementById('login-form');
|
||||
if (loginForm) loginForm.addEventListener('submit', handleLogin);
|
||||
|
||||
// Register form
|
||||
const registerForm = document.getElementById('register-form');
|
||||
if (registerForm) registerForm.addEventListener('submit', handleRegister);
|
||||
|
||||
// Forgot password form
|
||||
const forgotForm = document.getElementById('forgot-form');
|
||||
if (forgotForm) forgotForm.addEventListener('submit', handleForgotPassword);
|
||||
|
||||
// Profile form
|
||||
const profileForm = document.getElementById('profile-form');
|
||||
if (profileForm) profileForm.addEventListener('submit', handleProfileUpdate);
|
||||
|
||||
// Password form
|
||||
const passwordForm = document.getElementById('password-form');
|
||||
if (passwordForm) passwordForm.addEventListener('submit', handlePasswordChange);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Authentication
|
||||
// ============================================================
|
||||
|
||||
async function checkAuth() {
|
||||
if (!authToken) {
|
||||
showLoggedOutState();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${CONSENT_SERVICE_URL}/api/v1/auth/me`, {
|
||||
headers: { 'Authorization': `Bearer ${authToken}` }
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
currentUser = await res.json();
|
||||
showLoggedInState();
|
||||
} else {
|
||||
localStorage.removeItem('bp_token');
|
||||
authToken = null;
|
||||
showLoggedOutState();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Auth check failed:', err);
|
||||
showLoggedOutState();
|
||||
}
|
||||
}
|
||||
|
||||
function showLoggedInState() {
|
||||
document.getElementById('btn-login')?.classList.add('hidden');
|
||||
document.getElementById('user-menu')?.classList.remove('hidden');
|
||||
document.getElementById('welcome-section')?.classList.add('hidden');
|
||||
document.getElementById('dashboard')?.classList.remove('hidden');
|
||||
|
||||
if (currentUser) {
|
||||
const initials = getInitials(currentUser.name || currentUser.email);
|
||||
document.querySelectorAll('.user-avatar').forEach(el => el.textContent = initials);
|
||||
document.getElementById('user-name').textContent = currentUser.name || 'Benutzer';
|
||||
document.getElementById('dropdown-name').textContent = currentUser.name || 'Benutzer';
|
||||
document.getElementById('dropdown-email').textContent = currentUser.email;
|
||||
}
|
||||
|
||||
loadConsentCount();
|
||||
}
|
||||
|
||||
function showLoggedOutState() {
|
||||
document.getElementById('btn-login')?.classList.remove('hidden');
|
||||
document.getElementById('user-menu')?.classList.add('hidden');
|
||||
document.getElementById('welcome-section')?.classList.remove('hidden');
|
||||
document.getElementById('dashboard')?.classList.add('hidden');
|
||||
}
|
||||
|
||||
function getInitials(name) {
|
||||
if (!name) return 'BP';
|
||||
const parts = name.split(' ').filter(p => p.length > 0);
|
||||
if (parts.length >= 2) {
|
||||
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
||||
}
|
||||
return name.substring(0, 2).toUpperCase();
|
||||
}
|
||||
|
||||
async function handleLogin(e) {
|
||||
e.preventDefault();
|
||||
const email = document.getElementById('login-email').value;
|
||||
const password = document.getElementById('login-password').value;
|
||||
const messageEl = document.getElementById('login-message');
|
||||
|
||||
try {
|
||||
const res = await fetch(`${CONSENT_SERVICE_URL}/api/v1/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email, password })
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok) {
|
||||
authToken = data.token;
|
||||
localStorage.setItem('bp_token', authToken);
|
||||
currentUser = data.user;
|
||||
showMessage(messageEl, 'Erfolgreich angemeldet!', 'success');
|
||||
setTimeout(() => {
|
||||
closeAllModals();
|
||||
showLoggedInState();
|
||||
}, 500);
|
||||
} else {
|
||||
showMessage(messageEl, data.error || 'Anmeldung fehlgeschlagen', 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
showMessage(messageEl, 'Verbindungsfehler', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRegister(e) {
|
||||
e.preventDefault();
|
||||
const name = document.getElementById('register-name').value;
|
||||
const email = document.getElementById('register-email').value;
|
||||
const password = document.getElementById('register-password').value;
|
||||
const passwordConfirm = document.getElementById('register-password-confirm').value;
|
||||
const messageEl = document.getElementById('register-message');
|
||||
|
||||
if (password !== passwordConfirm) {
|
||||
showMessage(messageEl, 'Passwörter stimmen nicht überein', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${CONSENT_SERVICE_URL}/api/v1/auth/register`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, email, password })
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok) {
|
||||
showMessage(messageEl, 'Registrierung erfolgreich! Sie können sich jetzt anmelden.', 'success');
|
||||
setTimeout(() => switchAuthTab('login'), 1500);
|
||||
} else {
|
||||
showMessage(messageEl, data.error || 'Registrierung fehlgeschlagen', 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
showMessage(messageEl, 'Verbindungsfehler', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleForgotPassword(e) {
|
||||
e.preventDefault();
|
||||
const email = document.getElementById('forgot-email').value;
|
||||
const messageEl = document.getElementById('forgot-message');
|
||||
|
||||
try {
|
||||
const res = await fetch(`${CONSENT_SERVICE_URL}/api/v1/auth/request-password-reset`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email })
|
||||
});
|
||||
|
||||
showMessage(messageEl, 'Wenn ein Konto mit dieser E-Mail existiert, erhalten Sie einen Link zum Zurücksetzen.', 'success');
|
||||
} catch (err) {
|
||||
showMessage(messageEl, 'Verbindungsfehler', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function logout() {
|
||||
localStorage.removeItem('bp_token');
|
||||
authToken = null;
|
||||
currentUser = null;
|
||||
showLoggedOutState();
|
||||
document.getElementById('user-menu')?.classList.remove('open');
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Modals
|
||||
// ============================================================
|
||||
|
||||
function showLoginModal() {
|
||||
document.getElementById('login-modal')?.classList.add('active');
|
||||
switchAuthTab('login');
|
||||
}
|
||||
|
||||
function showMyConsents() {
|
||||
document.getElementById('consents-modal')?.classList.add('active');
|
||||
document.getElementById('user-menu')?.classList.remove('open');
|
||||
loadMyConsents();
|
||||
}
|
||||
|
||||
function showDataExport() {
|
||||
document.getElementById('export-modal')?.classList.add('active');
|
||||
document.getElementById('user-menu')?.classList.remove('open');
|
||||
loadExportRequests();
|
||||
}
|
||||
|
||||
function showAccountSettings() {
|
||||
document.getElementById('settings-modal')?.classList.add('active');
|
||||
document.getElementById('user-menu')?.classList.remove('open');
|
||||
|
||||
if (currentUser) {
|
||||
document.getElementById('profile-name').value = currentUser.name || '';
|
||||
document.getElementById('profile-email').value = currentUser.email || '';
|
||||
}
|
||||
}
|
||||
|
||||
function showLegalDocument(type) {
|
||||
const modal = document.getElementById('legal-modal');
|
||||
const titleEl = document.getElementById('legal-title');
|
||||
const loadingEl = document.getElementById('legal-loading');
|
||||
const contentEl = document.getElementById('legal-content');
|
||||
|
||||
const titles = {
|
||||
privacy: 'Datenschutzerklärung',
|
||||
terms: 'Allgemeine Geschäftsbedingungen',
|
||||
imprint: 'Impressum',
|
||||
cookies: 'Cookie-Richtlinie'
|
||||
};
|
||||
|
||||
titleEl.textContent = titles[type] || 'Dokument';
|
||||
loadingEl.style.display = 'block';
|
||||
contentEl.innerHTML = '';
|
||||
modal?.classList.add('active');
|
||||
|
||||
loadLegalDocument(type).then(html => {
|
||||
loadingEl.style.display = 'none';
|
||||
contentEl.innerHTML = html;
|
||||
}).catch(() => {
|
||||
loadingEl.style.display = 'none';
|
||||
contentEl.innerHTML = '<p>Dokument konnte nicht geladen werden.</p>';
|
||||
});
|
||||
}
|
||||
|
||||
function showForgotPassword() {
|
||||
document.getElementById('login-form')?.classList.add('hidden');
|
||||
document.getElementById('register-form')?.classList.add('hidden');
|
||||
document.getElementById('forgot-form')?.classList.remove('hidden');
|
||||
document.querySelectorAll('.auth-tab').forEach(t => t.classList.remove('active'));
|
||||
}
|
||||
|
||||
function closeAllModals() {
|
||||
document.querySelectorAll('.modal.active').forEach(m => m.classList.remove('active'));
|
||||
}
|
||||
|
||||
function switchAuthTab(tab) {
|
||||
document.querySelectorAll('.auth-tab').forEach(t => {
|
||||
t.classList.toggle('active', t.dataset.tab === tab);
|
||||
});
|
||||
document.getElementById('login-form')?.classList.toggle('hidden', tab !== 'login');
|
||||
document.getElementById('register-form')?.classList.toggle('hidden', tab !== 'register');
|
||||
document.getElementById('forgot-form')?.classList.add('hidden');
|
||||
|
||||
// Clear messages
|
||||
document.querySelectorAll('.form-message').forEach(m => {
|
||||
m.className = 'form-message';
|
||||
m.textContent = '';
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Consents
|
||||
// ============================================================
|
||||
|
||||
async function loadConsentCount() {
|
||||
if (!authToken) return;
|
||||
|
||||
try {
|
||||
const res = await fetch(`${CONSENT_SERVICE_URL}/api/v1/consents/my`, {
|
||||
headers: { 'Authorization': `Bearer ${authToken}` }
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
const count = data.consents?.length || 0;
|
||||
const badge = document.getElementById('consent-count');
|
||||
if (badge) badge.textContent = `${count} aktiv`;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load consent count:', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadMyConsents() {
|
||||
const loadingEl = document.getElementById('consents-loading');
|
||||
const listEl = document.getElementById('consents-list');
|
||||
const emptyEl = document.getElementById('consents-empty');
|
||||
|
||||
loadingEl.style.display = 'block';
|
||||
listEl.innerHTML = '';
|
||||
emptyEl?.classList.add('hidden');
|
||||
|
||||
try {
|
||||
const res = await fetch(`${CONSENT_SERVICE_URL}/api/v1/consents/my`, {
|
||||
headers: { 'Authorization': `Bearer ${authToken}` }
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
const consents = data.consents || [];
|
||||
|
||||
loadingEl.style.display = 'none';
|
||||
|
||||
if (consents.length === 0) {
|
||||
emptyEl?.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
listEl.innerHTML = consents.map(c => `
|
||||
<div class="consent-item">
|
||||
<div class="consent-info">
|
||||
<h3>${escapeHtml(c.document_name || c.document_type)}</h3>
|
||||
<p>Version ${c.version || '1.0'}</p>
|
||||
</div>
|
||||
<div class="consent-date">
|
||||
Zugestimmt am ${formatDate(c.created_at)}
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
loadingEl.style.display = 'none';
|
||||
listEl.innerHTML = '<p>Fehler beim Laden der Zustimmungen.</p>';
|
||||
}
|
||||
} catch (err) {
|
||||
loadingEl.style.display = 'none';
|
||||
listEl.innerHTML = '<p>Verbindungsfehler.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Data Export (GDPR)
|
||||
// ============================================================
|
||||
|
||||
async function requestDataExport() {
|
||||
const messageEl = document.getElementById('export-message');
|
||||
const btn = document.getElementById('btn-request-export');
|
||||
|
||||
btn.disabled = true;
|
||||
|
||||
try {
|
||||
const res = await fetch(`${BACKEND_URL}/api/gdpr/request-export`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${authToken}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
showMessage(messageEl, 'Ihre Anfrage wurde erfolgreich eingereicht. Sie erhalten eine E-Mail, sobald der Export bereit ist.', 'success');
|
||||
loadExportRequests();
|
||||
} else {
|
||||
const data = await res.json();
|
||||
showMessage(messageEl, data.error || 'Anfrage fehlgeschlagen', 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
showMessage(messageEl, 'Verbindungsfehler', 'error');
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadExportRequests() {
|
||||
const statusEl = document.getElementById('export-status');
|
||||
const listEl = document.getElementById('export-requests-list');
|
||||
|
||||
if (!statusEl || !listEl) return;
|
||||
|
||||
try {
|
||||
const res = await fetch(`${BACKEND_URL}/api/gdpr/my-requests`, {
|
||||
headers: { 'Authorization': `Bearer ${authToken}` }
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
const requests = data.requests || [];
|
||||
|
||||
if (requests.length > 0) {
|
||||
statusEl.classList.remove('hidden');
|
||||
listEl.innerHTML = requests.map(r => `
|
||||
<div class="consent-item">
|
||||
<div class="consent-info">
|
||||
<h3>${r.type === 'export' ? 'Datenexport' : 'Datenlöschung'}</h3>
|
||||
<p>Status: ${r.status}</p>
|
||||
</div>
|
||||
<div class="consent-date">
|
||||
${formatDate(r.created_at)}
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
statusEl.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load export requests:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Legal Documents
|
||||
// ============================================================
|
||||
|
||||
async function loadLegalDocument(type) {
|
||||
try {
|
||||
const res = await fetch(`${CONSENT_SERVICE_URL}/api/v1/documents/type/${type}/published`);
|
||||
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
return data.content || '<p>Dokument ist leer.</p>';
|
||||
}
|
||||
|
||||
return '<p>Dokument nicht gefunden.</p>';
|
||||
} catch (err) {
|
||||
return '<p>Fehler beim Laden.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Profile & Settings
|
||||
// ============================================================
|
||||
|
||||
async function handleProfileUpdate(e) {
|
||||
e.preventDefault();
|
||||
const name = document.getElementById('profile-name').value;
|
||||
const messageEl = document.getElementById('profile-message');
|
||||
|
||||
try {
|
||||
const res = await fetch(`${CONSENT_SERVICE_URL}/api/v1/auth/update-profile`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${authToken}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ name })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
currentUser.name = name;
|
||||
showMessage(messageEl, 'Profil aktualisiert', 'success');
|
||||
showLoggedInState();
|
||||
} else {
|
||||
showMessage(messageEl, 'Aktualisierung fehlgeschlagen', 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
showMessage(messageEl, 'Verbindungsfehler', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePasswordChange(e) {
|
||||
e.preventDefault();
|
||||
const currentPassword = document.getElementById('current-password').value;
|
||||
const newPassword = document.getElementById('new-password').value;
|
||||
const confirmPassword = document.getElementById('new-password-confirm').value;
|
||||
const messageEl = document.getElementById('password-message');
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
showMessage(messageEl, 'Passwörter stimmen nicht überein', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${CONSENT_SERVICE_URL}/api/v1/auth/change-password`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${authToken}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
current_password: currentPassword,
|
||||
new_password: newPassword
|
||||
})
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
showMessage(messageEl, 'Passwort geändert', 'success');
|
||||
document.getElementById('password-form').reset();
|
||||
} else {
|
||||
const data = await res.json();
|
||||
showMessage(messageEl, data.error || 'Änderung fehlgeschlagen', 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
showMessage(messageEl, 'Verbindungsfehler', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function confirmDeleteAccount() {
|
||||
if (confirm('Sind Sie sicher, dass Sie Ihr Konto löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.')) {
|
||||
if (confirm('Letzte Warnung: Alle Ihre Daten werden unwiderruflich gelöscht. Fortfahren?')) {
|
||||
deleteAccount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteAccount() {
|
||||
try {
|
||||
const res = await fetch(`${CONSENT_SERVICE_URL}/api/v1/auth/delete-account`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${authToken}` }
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
alert('Ihr Konto wurde gelöscht.');
|
||||
logout();
|
||||
closeAllModals();
|
||||
} else {
|
||||
alert('Kontoloöschung fehlgeschlagen.');
|
||||
}
|
||||
} catch (err) {
|
||||
alert('Verbindungsfehler');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Utilities
|
||||
// ============================================================
|
||||
|
||||
function showMessage(el, text, type) {
|
||||
if (!el) return;
|
||||
el.textContent = text;
|
||||
el.className = `form-message ${type}`;
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
const div = document.createElement('div');
|
||||
div.textContent = str;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function formatDate(dateStr) {
|
||||
if (!dateStr) return '-';
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric'
|
||||
});
|
||||
}
|
||||
|
||||
// Export for global access
|
||||
window.showLoginModal = showLoginModal;
|
||||
window.showMyConsents = showMyConsents;
|
||||
window.showDataExport = showDataExport;
|
||||
window.showAccountSettings = showAccountSettings;
|
||||
window.showLegalDocument = showLegalDocument;
|
||||
window.showForgotPassword = showForgotPassword;
|
||||
window.closeAllModals = closeAllModals;
|
||||
window.switchAuthTab = switchAuthTab;
|
||||
window.logout = logout;
|
||||
window.requestDataExport = requestDataExport;
|
||||
window.confirmDeleteAccount = confirmDeleteAccount;
|
||||
Reference in New Issue
Block a user