Refactor backend CORS settings to include default origins and improve error handling in chat services: Introduce dynamic CORS origin handling, enhance RabbitMQ message sending with fallback mechanisms, and update WebSocket service to manage pending messages. Update UI components for better accessibility and responsiveness, including adjustments to dialog and navigation elements. Enhance styling for improved user experience across various components.

This commit is contained in:
Torsten Schulz (local)
2026-03-19 14:44:04 +01:00
parent 70ee17a48a
commit b0c8f01fde
67 changed files with 5426 additions and 1099 deletions

View File

@@ -1,45 +1,66 @@
<template>
<div>
<h2>{{ $t("settings.account.title") }}</h2>
<div>
<label><span>{{ $t("settings.account.username") }} </span><input type="text" v-model="username"
:placeholder="$t('settings.account.username')" /></label>
</div>
<div>
<label><span>{{ $t("settings.account.email") }} </span><input type="text" v-model="email"
:placeholder="$t('settings.account.email')" /></label>
</div>
<div>
<label><span>{{ $t("settings.account.newpassword") }} </span><input type="password" v-model="newpassword"
:placeholder="$t('settings.account.newpassword')" autocomplete="new-password" /></label>
</div>
<div>
<label><span>{{ $t("settings.account.newpasswordretype") }} </span><input type="password"
v-model="newpasswordretype" :placeholder="$t('settings.account.newpasswordretype')" autocomplete="new-password" /></label>
</div>
<div>
<label><span>{{ $t("settings.account.oldpassword") }} </span><input type="password"
v-model="oldpassword" :placeholder="$t('settings.account.oldpassword')" autocomplete="current-password" /></label>
</div>
<div>
<button @click="changeAccount">{{ $t("settings.account.changeaction") }}</button>
</div>
<div>
<label><input type="checkbox" v-model="showInSearch" /> {{ $t("settings.account.showinsearch") }}</label>
</div>
<div class="account-settings">
<section class="account-settings__hero surface-card">
<span class="account-settings__eyebrow">Einstellungen</span>
<h2>{{ $t("settings.account.title") }}</h2>
<p>Benutzername, E-Mail, Passwort und Sichtbarkeit an einer Stelle pflegen.</p>
</section>
<section class="account-settings__panel surface-card">
<div class="account-settings__grid">
<label class="account-settings__field">
<span>{{ $t("settings.account.username") }}</span>
<input type="text" v-model="username" :placeholder="$t('settings.account.username')" />
</label>
<label class="account-settings__field">
<span>{{ $t("settings.account.email") }}</span>
<input type="text" v-model="email" :placeholder="$t('settings.account.email')" />
</label>
<label class="account-settings__field">
<span>{{ $t("settings.account.newpassword") }}</span>
<input type="password" v-model="newpassword" :placeholder="$t('settings.account.newpassword')"
autocomplete="new-password" :class="{ 'field-error': newpassword && !isNewPasswordValid }" />
<span v-if="newpassword && !isNewPasswordValid" class="form-error">Das neue Passwort sollte mindestens 8 Zeichen haben.</span>
</label>
<label class="account-settings__field">
<span>{{ $t("settings.account.newpasswordretype") }}</span>
<input type="password" v-model="newpasswordretype"
:placeholder="$t('settings.account.newpasswordretype')" autocomplete="new-password"
:class="{ 'field-error': newpasswordretype && !passwordsMatch }" />
<span v-if="newpasswordretype && !passwordsMatch" class="form-error">Die Passwoerter stimmen nicht ueberein.</span>
</label>
<label class="account-settings__field account-settings__field--full">
<span>{{ $t("settings.account.oldpassword") }}</span>
<input type="password" v-model="oldpassword" :placeholder="$t('settings.account.oldpassword')"
autocomplete="current-password" :class="{ 'field-error': requiresOldPassword && !oldpassword.trim() }" />
<span v-if="requiresOldPassword && !oldpassword.trim()" class="form-error">Zum Passwortwechsel wird das aktuelle Passwort benoetigt.</span>
</label>
</div>
<label class="account-settings__toggle">
<input type="checkbox" v-model="showInSearch" />
<span>{{ $t("settings.account.showinsearch") }}</span>
</label>
<div class="account-settings__actions">
<button @click="changeAccount">{{ $t("settings.account.changeaction") }}</button>
</div>
</section>
</div>
</template>
<script>
import apiClient from '@/utils/axios.js';
import { mapGetters } from 'vuex';
import { showApiError, showError, showSuccess } from '@/utils/feedback.js';
export default {
name: "AccountSettingsView",
components: {},
computed: {
...mapGetters(['user']),
},
data() {
return {
username: "",
@@ -50,6 +71,18 @@ export default {
oldpassword: "",
};
},
computed: {
...mapGetters(['user']),
requiresOldPassword() {
return this.newpassword.trim().length > 0;
},
isNewPasswordValid() {
return this.newpassword.length === 0 || this.newpassword.length >= 8;
},
passwordsMatch() {
return this.newpassword === this.newpasswordretype;
}
},
methods: {
async changeAccount() {
try {
@@ -57,15 +90,19 @@ export default {
const hasNewPassword = this.newpassword && this.newpassword.trim() !== '';
if (hasNewPassword) {
if (!this.isNewPasswordValid) {
showError(this, 'Das neue Passwort ist noch zu kurz.');
return;
}
// Validiere Passwort-Wiederholung nur wenn ein neues Passwort eingegeben wurde
if (this.newpassword !== this.newpasswordretype) {
alert('Die Passwörter stimmen nicht überein.');
if (!this.passwordsMatch) {
showError(this, 'Die Passwoerter stimmen nicht ueberein.');
return;
}
// Prüfe ob das alte Passwort eingegeben wurde
if (!this.oldpassword || this.oldpassword.trim() === '') {
alert('Bitte geben Sie Ihr aktuelles Passwort ein, um das Passwort zu ändern.');
showError(this, 'Bitte geben Sie Ihr aktuelles Passwort ein, um das Passwort zu aendern.');
return;
}
}
@@ -89,7 +126,7 @@ export default {
// API-Aufruf zum Speichern der Account-Einstellungen
await apiClient.post('/api/settings/set-account', accountData);
alert('Account-Einstellungen erfolgreich gespeichert!');
showSuccess(this, 'Account-Einstellungen erfolgreich gespeichert.');
// Leere die Passwort-Felder nach erfolgreichem Speichern
this.newpassword = '';
@@ -98,17 +135,12 @@ export default {
} catch (error) {
console.error('Fehler beim Speichern der Account-Einstellungen:', error);
if (error.response && error.response.data && error.response.data.error) {
alert('Fehler: ' + error.response.data.error);
} else {
alert('Ein Fehler ist aufgetreten beim Speichern der Account-Einstellungen.');
}
showApiError(this, error, 'Ein Fehler ist beim Speichern der Account-Einstellungen aufgetreten.');
}
}
},
async mounted() {
const response = await apiClient.post('/api/settings/account', { userId: this.user.id });
console.log(response.data);
this.username = response.data.username;
this.showInSearch = response.data.showinsearch;
this.email = response.data.email;
@@ -117,18 +149,101 @@ export default {
this.newpassword = '';
this.newpasswordretype = '';
this.oldpassword = '';
console.log(this.showInSearch);
},
};
</script>
<style lang="scss" scoped>
label {
white-space: nowrap;
.account-settings {
display: grid;
gap: 18px;
max-width: 960px;
}
label > span {
width: 15em;
display: inline-block;
.account-settings__hero,
.account-settings__panel {
background: linear-gradient(180deg, rgba(255, 252, 247, 0.97), rgba(250, 244, 235, 0.95));
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-soft);
}
</style>
.account-settings__hero {
padding: 26px 28px;
}
.account-settings__eyebrow {
display: inline-flex;
margin-bottom: 8px;
padding: 4px 10px;
border-radius: var(--radius-pill);
background: var(--color-primary-soft);
color: var(--color-text-secondary);
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.account-settings__hero p {
margin: 0;
color: var(--color-text-secondary);
}
.account-settings__panel {
padding: 24px;
}
.account-settings__grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
}
.account-settings__field {
display: grid;
gap: 8px;
}
.account-settings__field span {
font-weight: 600;
color: var(--color-text-secondary);
}
.account-settings__field--full {
grid-column: 1 / -1;
}
.account-settings__toggle {
display: inline-flex;
align-items: center;
gap: 10px;
margin-top: 18px;
color: var(--color-text-secondary);
}
.account-settings__actions {
display: flex;
justify-content: flex-end;
margin-top: 18px;
}
@media (max-width: 760px) {
.account-settings__grid {
grid-template-columns: 1fr;
}
.account-settings__hero,
.account-settings__panel {
padding: 20px;
}
.account-settings__actions {
justify-content: stretch;
}
.account-settings__actions button {
width: 100%;
}
}
</style>