Files
crm-system/app/webui/settings-profile.html

149 lines
6.3 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>CRM Mein Profil</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="/css/app.css" />
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js"></script>
<script type="module" src="/js/store.js"></script>
<script type="module" src="/js/components/notifications.js"></script>
</head>
<body class="min-h-screen bg-slate-50">
<div x-data="toastContainer()" class="crm-toast-container" x-cloak>
<template x-for="n in items" :key="n.id">
<div class="crm-toast" :class="'toast-' + n.type"
@click="dismiss(n.id)" x-text="n.message"></div>
</template>
</div>
<div x-data="{ init: () => Alpine.store('auth').init() }" x-init="init()" class="flex min-h-screen">
<aside class="crm-sidebar">
<div class="px-6 py-4 text-white text-lg font-bold border-b border-slate-700">
<a href="/app.html" class="!text-white !border-0">CRM</a>
</div>
<nav class="mt-2">
<a href="/app.html">Dashboard</a>
<a href="/accounts.html">Accounts</a>
<a href="/contacts.html">Contacts</a>
<a href="/pipeline.html">Pipeline</a>
<a href="/activities.html">Activities</a>
<a href="/settings-profile.html" class="active">Profil</a>
<a href="/settings-users.html" x-show="$store.auth.isAdmin">Users (Admin)</a>
<a href="/settings-org.html" x-show="$store.auth.isAdmin">Org (Admin)</a>
</nav>
</aside>
<div class="flex-1 flex flex-col">
<header class="bg-white border-b border-slate-200 px-6 py-3 flex items-center justify-between">
<h1 class="text-xl font-semibold text-slate-800">Mein Profil</h1>
<div class="flex items-center gap-3 text-sm">
<span class="text-slate-600">
<strong class="text-slate-900"
x-text="$store.auth.user ? $store.auth.user.name : '…'"></strong>
</span>
<button class="btn-secondary" @click="$store.auth.logout()">Logout</button>
</div>
</header>
<main class="flex-1 p-6 max-w-2xl" x-data="profileForm()" x-init="init()">
<div x-show="loading" class="crm-card text-center py-8">
<div class="crm-spinner mx-auto"></div>
</div>
<div x-show="!loading && user" class="crm-card">
<h2 class="text-lg font-semibold mb-4">Profil bearbeiten</h2>
<!-- Read-only fields -->
<div class="grid grid-cols-2 gap-3 mb-4 text-sm">
<div>
<label class="crm-label">E-Mail (nicht änderbar)</label>
<input type="email" disabled :value="user?.email || ''" class="crm-input" />
</div>
<div>
<label class="crm-label">Rolle (nicht änderbar)</label>
<input type="text" disabled :value="user?.role || ''" class="crm-input" />
</div>
</div>
<form @submit.prevent="submit()">
<div class="mb-3">
<label class="crm-label">Name *</label>
<input type="text" required minlength="1" maxlength="255"
x-model="form.name" class="crm-input" />
</div>
<div class="mb-3">
<label class="crm-label">Avatar-URL</label>
<input type="text" maxlength="1024"
x-model="form.avatar_url" class="crm-input" />
</div>
<div class="mb-3 flex items-center gap-2">
<input type="checkbox" id="notif" x-model="form.email_notifications" />
<label for="notif" class="text-sm">E-Mail-Benachrichtigungen erhalten</label>
</div>
<div x-show="error" class="mb-3 p-2 bg-red-50 text-red-700 text-sm rounded"
x-text="error"></div>
<div class="flex gap-2 justify-end mt-4">
<button type="submit" :disabled="saving" class="btn-primary"
x-text="saving ? 'Speichere …' : 'Speichern'"></button>
</div>
</form>
</div>
</main>
</div>
</div>
<script>
function profileForm() {
return {
user: null,
loading: true,
saving: false,
error: '',
form: { name: '', avatar_url: '', email_notifications: true },
async init() {
// wait for auth store to load user
const auth = Alpine.store('auth');
if (!auth.user) {
try { await auth.fetchUser(); } catch { /* 401 → redirect */ }
}
this.user = auth.user;
if (this.user) {
this.form.name = this.user.name || '';
this.form.avatar_url = this.user.avatar_url || '';
this.form.email_notifications = this.user.email_notifications !== false;
}
this.loading = false;
},
async submit() {
this.saving = true; this.error = '';
try {
const { api } = await import('/js/api.js');
const payload = {
name: this.form.name,
email_notifications: this.form.email_notifications,
};
if (this.form.avatar_url) payload.avatar_url = this.form.avatar_url;
const updated = await api.patch(`/users/${this.user.id}`, payload);
Alpine.store('auth').user = updated;
Alpine.store('notifications').success('Profil aktualisiert.');
} catch (err) {
this.error = err.message || 'Fehler beim Speichern.';
} finally { this.saving = false; }
},
};
}
</script>
</body>
</html>