Files
whereismymoney/internal/views/settings.templ
T
Matthias Hinrichs 189e7a2329 first commit
2025-08-26 03:17:49 +02:00

313 lines
12 KiB
Plaintext

package views
import (
"whereismymoney/internal/models"
"fmt"
)
templ Settings(userName string, user models.User, categories []models.Category, bankAccounts []models.BankAccount) {
@Layout("Einstellungen - WhereIsMyMoney") {
<div class="min-h-screen bg-gray-50">
@Navigation(userName)
<div class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<div class="px-4 py-6 sm:px-0">
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900">Einstellungen</h1>
<p class="mt-2 text-gray-600">Verwalte deine Kontoinformationen und App-Einstellungen</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Benutzereinstellungen -->
<div class="lg:col-span-2">
<div class="bg-white shadow rounded-lg">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-medium text-gray-900">Benutzereinstellungen</h2>
</div>
<div class="px-6 py-4">
<form id="userSettingsForm" class="space-y-4">
<div>
<label for="username" class="block text-sm font-medium text-gray-700">Benutzername</label>
<input type="text" id="username" name="username" value={ user.Name } class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500">
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700">E-Mail</label>
<input type="email" id="email" name="email" value={ user.Email } readonly class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-50 text-gray-500 cursor-not-allowed" title="E-Mail-Adresse kann nicht geändert werden">
<p class="mt-1 text-xs text-gray-500">Die E-Mail-Adresse dient als eindeutige Benutzer-ID und kann nicht geändert werden.</p>
</div>
<div class="pt-4">
<button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500">
Speichern
</button>
</div>
</form>
</div>
</div>
<!-- Passwort ändern -->
<div class="bg-white shadow rounded-lg mt-6">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-medium text-gray-900">Passwort ändern</h2>
</div>
<div class="px-6 py-4">
<form id="passwordForm" class="space-y-4">
<div>
<label for="current_password" class="block text-sm font-medium text-gray-700">Aktuelles Passwort</label>
<input type="password" id="current_password" name="current_password" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500">
</div>
<div>
<label for="new_password" class="block text-sm font-medium text-gray-700">Neues Passwort</label>
<input type="password" id="new_password" name="new_password" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500">
</div>
<div>
<label for="confirm_password" class="block text-sm font-medium text-gray-700">Passwort bestätigen</label>
<input type="password" id="confirm_password" name="confirm_password" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500">
</div>
<div class="pt-4">
<button type="submit" class="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500">
Passwort ändern
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Seitenleiste -->
<div class="space-y-6">
<!-- Kategorien verwalten -->
<div class="bg-white shadow rounded-lg">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-medium text-gray-900">Kategorien</h3>
</div>
<div class="px-6 py-4">
<div class="space-y-2 max-h-48 overflow-y-auto">
for _, category := range categories {
<div class="flex items-center justify-between p-2 hover:bg-gray-50 rounded">
<span class="text-sm">{ category.Icon } { category.Name }</span>
<button class="delete-category text-red-600 hover:text-red-800 text-sm" data-category-id={ fmt.Sprintf("%d", category.ID) }>
🗑️
</button>
</div>
}
</div>
<button onclick="showModal('categoryModal')" class="mt-4 w-full px-3 py-2 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200 text-sm">
+ Neue Kategorie
</button>
</div>
</div>
<!-- Konten verwalten -->
<div class="bg-white shadow rounded-lg">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-medium text-gray-900">Bankkonten</h3>
</div>
<div class="px-6 py-4">
<div class="space-y-2 max-h-48 overflow-y-auto">
for _, account := range bankAccounts {
<div class="flex items-center justify-between p-2 hover:bg-gray-50 rounded">
<div class="text-sm">
<div class="font-medium">{ account.Name }</div>
<div class="text-gray-500">{ account.Bank }</div>
</div>
<button class="delete-account text-red-600 hover:text-red-800 text-sm" data-account-id={ fmt.Sprintf("%d", account.ID) }>
🗑️
</button>
</div>
}
</div>
<a href="/accounts" class="mt-4 w-full block px-3 py-2 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200 text-sm text-center">
Konten verwalten
</a>
</div>
</div>
<!-- App-Informationen -->
<div class="bg-white shadow rounded-lg">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-medium text-gray-900">App-Informationen</h3>
</div>
<div class="px-6 py-4 space-y-2 text-sm text-gray-600">
<div>Version: 1.0.0</div>
<div>Erstellt mit Go & Templ</div>
<div>© 2025 WhereIsMyMoney</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Kategorie Modal -->
<div id="categoryModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden z-50">
<div class="flex items-center justify-center min-h-screen px-4">
<div class="bg-white rounded-lg shadow-xl max-w-md w-full">
<form id="categoryForm" method="POST" action="/settings/categories">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-medium text-gray-900">Neue Kategorie erstellen</h3>
</div>
<div class="px-6 py-4 space-y-4">
<div>
<label for="category_name" class="block text-sm font-medium text-gray-700">Name</label>
<input type="text" id="category_name" name="name" required class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500" placeholder="z.B. Lebensmittel">
</div>
<div>
<label for="category_icon" class="block text-sm font-medium text-gray-700">Icon (Emoji)</label>
<input type="text" id="category_icon" name="icon" required class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500" placeholder="🛒" maxlength="2">
</div>
<div>
<label for="category_color" class="block text-sm font-medium text-gray-700">Farbe</label>
<input type="color" id="category_color" name="color" value="#3B82F6" class="w-full h-10 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500">
</div>
</div>
<div class="px-6 py-4 border-t border-gray-200 flex justify-end space-x-3">
<button type="button" onclick="hideModal('categoryModal')" class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200">
Abbrechen
</button>
<button type="submit" class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700">
Erstellen
</button>
</div>
</form>
</div>
</div>
</div>
<script>
function showModal(modalId) {
document.getElementById(modalId).classList.remove('hidden');
}
function hideModal(modalId) {
document.getElementById(modalId).classList.add('hidden');
}
// Event Listeners für Delete-Buttons
document.addEventListener('DOMContentLoaded', function() {
// Delete Category Buttons
document.querySelectorAll('.delete-category').forEach(button => {
button.addEventListener('click', function() {
const categoryId = this.getAttribute('data-category-id');
deleteCategory(categoryId);
});
});
// Delete Account Buttons
document.querySelectorAll('.delete-account').forEach(button => {
button.addEventListener('click', function() {
const accountId = this.getAttribute('data-account-id');
deleteAccount(accountId);
});
});
});
function deleteCategory(categoryId) {
if (!confirm('Möchten Sie diese Kategorie wirklich löschen?')) {
return;
}
fetch(`/settings/categories/${categoryId}`, {
method: 'DELETE'
}).then(response => {
if (response.ok) {
location.reload();
} else {
alert('Fehler beim Löschen der Kategorie');
}
});
}
function deleteAccount(accountId) {
if (!confirm('Möchten Sie dieses Konto wirklich löschen?')) {
return;
}
fetch(`/settings/accounts/${accountId}`, {
method: 'DELETE'
}).then(response => {
if (response.ok) {
location.reload();
} else {
alert('Fehler beim Löschen des Kontos');
}
});
}
// Benutzereinstellungen speichern
document.getElementById('userSettingsForm').addEventListener('submit', async function(e) {
e.preventDefault();
// Nur den Benutzernamen senden, E-Mail wird ignoriert
const formObject = {
username: document.getElementById('username').value
};
try {
const response = await fetch('/settings/user', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formObject)
});
if (response.ok) {
alert('Einstellungen gespeichert!');
} else {
alert('Fehler beim Speichern der Einstellungen');
}
} catch (error) {
alert('Fehler beim Speichern der Einstellungen');
}
});
// Passwort ändern
document.getElementById('passwordForm').addEventListener('submit', async function(e) {
e.preventDefault();
const newPassword = document.getElementById('new_password').value;
const confirmPassword = document.getElementById('confirm_password').value;
if (newPassword !== confirmPassword) {
alert('Die Passwörter stimmen nicht überein!');
return;
}
const formData = new FormData(this);
const formObject = {};
for (let [key, value] of formData.entries()) {
formObject[key] = value;
}
try {
const response = await fetch('/settings/password', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formObject)
});
if (response.ok) {
alert('Passwort erfolgreich geändert!');
this.reset();
} else {
const error = await response.text();
alert('Fehler: ' + error);
}
} catch (error) {
alert('Fehler beim Ändern des Passworts');
}
});
// Modal schließen beim Klick außerhalb
document.addEventListener('click', function(event) {
if (event.target.classList.contains('bg-opacity-50')) {
event.target.classList.add('hidden');
}
});
</script>
}
}