Initial commit

This commit is contained in:
iulian 2025-12-26 00:52:56 +00:00
commit 983cee0320
322 changed files with 57174 additions and 0 deletions

View file

@ -0,0 +1,265 @@
// Settings Page Functionality
document.addEventListener('DOMContentLoaded', () => {
setupAvatarHandlers();
setupProfileHandlers();
setupPasswordHandlers();
});
// Avatar upload and selection
function setupAvatarHandlers() {
const uploadBtn = document.getElementById('upload-avatar-btn');
const avatarInput = document.getElementById('avatar-upload');
const currentAvatar = document.getElementById('current-avatar');
const sidebarAvatar = document.getElementById('sidebar-avatar');
const defaultAvatarBtns = document.querySelectorAll('.default-avatar-btn');
// Trigger file input when upload button clicked
if (uploadBtn && avatarInput) {
uploadBtn.addEventListener('click', () => {
avatarInput.click();
});
// Handle file selection
avatarInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
// Validate file type
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp'];
if (!allowedTypes.includes(file.type)) {
showNotification('error', 'Invalid file type. Please use PNG, JPG, GIF, or WEBP.');
return;
}
// Validate file size (20MB)
if (file.size > 20 * 1024 * 1024) {
showNotification('error', 'File too large. Maximum size is 20MB.');
return;
}
// Upload avatar
const formData = new FormData();
formData.append('avatar', file);
try {
const response = await fetch('/api/settings/avatar', {
method: 'POST',
body: formData
});
const result = await response.json();
if (response.ok && result.success) {
// Update avatar displays
const avatarUrl = result.avatar.startsWith('icons/')
? `/static/${result.avatar}?t=${Date.now()}`
: `/${result.avatar}?t=${Date.now()}`;
currentAvatar.src = avatarUrl;
if (sidebarAvatar) sidebarAvatar.src = avatarUrl;
showNotification('success', result.message || 'Avatar updated successfully!');
} else {
showNotification('error', result.error || 'Failed to upload avatar');
}
} catch (error) {
console.error('Upload error:', error);
showNotification('error', 'An error occurred during upload');
}
// Reset input
avatarInput.value = '';
});
}
// Handle default avatar selection
defaultAvatarBtns.forEach(btn => {
btn.addEventListener('click', async () => {
const avatarPath = btn.getAttribute('data-avatar');
try {
const response = await fetch('/api/settings/avatar/default', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ avatar: avatarPath })
});
const result = await response.json();
if (response.ok && result.success) {
// Update avatar displays
const avatarUrl = result.avatar.startsWith('icons/')
? `/static/${result.avatar}?t=${Date.now()}`
: `/${result.avatar}?t=${Date.now()}`;
currentAvatar.src = avatarUrl;
if (sidebarAvatar) sidebarAvatar.src = avatarUrl;
// Update active state
defaultAvatarBtns.forEach(b => b.classList.remove('border-primary'));
btn.classList.add('border-primary');
showNotification('success', result.message || 'Avatar updated successfully!');
} else {
showNotification('error', result.error || 'Failed to update avatar');
}
} catch (error) {
console.error('Update error:', error);
showNotification('error', 'An error occurred');
}
});
});
}
// Profile update handlers
function setupProfileHandlers() {
const saveBtn = document.getElementById('save-profile-btn');
if (saveBtn) {
saveBtn.addEventListener('click', async () => {
const username = document.getElementById('username').value.trim();
const email = document.getElementById('email').value.trim();
const language = document.getElementById('language').value;
const currency = document.getElementById('currency').value;
if (!username || !email) {
showNotification('error', 'Username and email are required');
return;
}
// Email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
showNotification('error', 'Please enter a valid email address');
return;
}
try {
const response = await fetch('/api/settings/profile', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username,
email,
language,
currency
})
});
const result = await response.json();
if (response.ok && result.success) {
showNotification('success', result.message || 'Profile updated successfully!');
// Update language if changed
const currentLang = getCurrentLanguage();
if (language !== currentLang) {
setLanguage(language);
// Reload page to apply translations
setTimeout(() => {
window.location.reload();
}, 1000);
}
} else {
showNotification('error', result.error || 'Failed to update profile');
}
} catch (error) {
console.error('Update error:', error);
showNotification('error', 'An error occurred');
}
});
}
}
// Password change handlers
function setupPasswordHandlers() {
const changeBtn = document.getElementById('change-password-btn');
if (changeBtn) {
changeBtn.addEventListener('click', async () => {
const currentPassword = document.getElementById('current-password').value;
const newPassword = document.getElementById('new-password').value;
const confirmPassword = document.getElementById('confirm-password').value;
if (!currentPassword || !newPassword || !confirmPassword) {
showNotification('error', 'All password fields are required');
return;
}
if (newPassword.length < 6) {
showNotification('error', 'New password must be at least 6 characters');
return;
}
if (newPassword !== confirmPassword) {
showNotification('error', 'New passwords do not match');
return;
}
try {
const response = await fetch('/api/settings/password', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
current_password: currentPassword,
new_password: newPassword
})
});
const result = await response.json();
if (response.ok && result.success) {
showNotification('success', result.message || 'Password changed successfully!');
// Clear form
document.getElementById('current-password').value = '';
document.getElementById('new-password').value = '';
document.getElementById('confirm-password').value = '';
} else {
showNotification('error', result.error || 'Failed to change password');
}
} catch (error) {
console.error('Change password error:', error);
showNotification('error', 'An error occurred');
}
});
}
}
// Show notification
function showNotification(type, message) {
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 z-50 px-4 py-3 rounded-lg shadow-lg flex items-center gap-3 animate-slideIn ${
type === 'success'
? 'bg-green-500 text-white'
: 'bg-red-500 text-white'
}`;
notification.innerHTML = `
<span class="material-symbols-outlined text-[20px]">
${type === 'success' ? 'check_circle' : 'error'}
</span>
<span class="text-sm font-medium">${escapeHtml(message)}</span>
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('animate-slideOut');
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 3000);
}
// Escape HTML
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}